Skip to content

Commit 6eb2f6e

Browse files
authored
Merge pull request #46 from nim-lang/devel
Devel
2 parents d948370 + 7c60860 commit 6eb2f6e

File tree

6 files changed

+102
-51
lines changed

6 files changed

+102
-51
lines changed

README.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ $ nimble install zmq
3131
close(responder)
3232
```
3333

34-
#### client
34+
#### client
3535

3636
```nim
3737
import zmq
@@ -49,3 +49,12 @@ $ nimble install zmq
4949
For more examples demonstrating many functionalities and patterns that ZMQ offers, see the ``tests/`` and ``examples/`` folder.
5050

5151
The examples are commented to better understand how zmq works.
52+
53+
54+
### Log EAGAIN errno
55+
56+
Sometimes EAGAIN error happens in ZMQ context; typically this is a non-ctritical error that can be ignored. Nonetheless, if you desire to log or display such error, it is possible to enable it using the ``enableLogEagain`` and disable it with ``disableLogEagain``.
57+
58+
### Setting default flag as DONTWAIT
59+
60+
The default flag passed to send / receive is NOFLAGS. This can be overriden by defining ``-d:defaultFlagDontWait``

tests/async_demo.nim

+24-14
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,18 @@ proc asyncpoll() =
2222
puller2,
2323
ZMQ_POLLIN,
2424
proc(x: ZSocket) =
25-
let msg = x.receive()
26-
inc(msgCount)
27-
if msglist.contains(msg):
28-
msglist.delete(0)
29-
assert true
30-
else:
31-
assert false
25+
let
26+
# Avoid using indefinitly blocking proc in async context
27+
res = x.waitForReceive(timeout=10)
28+
if res.msgAvailable:
29+
let
30+
msg = res.msg
31+
inc(msgCount)
32+
if msglist.contains(msg):
33+
msglist.delete(0)
34+
assert true
35+
else:
36+
assert false
3237
)
3338
# assert message received are correct (should be even integer in string format)
3439
var msglist2 = @["0", "2", "4", "6", "8"]
@@ -37,13 +42,18 @@ proc asyncpoll() =
3742
puller,
3843
ZMQ_POLLIN,
3944
proc(x: ZSocket) =
40-
let msg = x.receive()
41-
inc(msgCount2)
42-
if msglist2.contains(msg):
43-
msglist2.delete(0)
44-
assert true
45-
else:
46-
assert false
45+
let
46+
# Avoid using indefinitly blocking proc in async context
47+
res = x.waitForReceive(timeout=10)
48+
if res.msgAvailable:
49+
let
50+
msg = res.msg
51+
inc(msgCount2)
52+
if msglist2.contains(msg):
53+
msglist2.delete(0)
54+
assert true
55+
else:
56+
assert false
4757
)
4858

4959
let

tests/tzmq.nim

+18-14
Original file line numberDiff line numberDiff line change
@@ -219,13 +219,15 @@ proc asyncpoll() =
219219
puller2,
220220
ZMQ_POLLIN,
221221
proc(x: ZSocket) =
222-
let msg = x.receive()
223-
inc(msgCount)
224-
if msglist.contains(msg):
225-
msglist.delete(0)
226-
check true
227-
else:
228-
check false
222+
let res= x.tryReceive()
223+
if res.msgAvailable:
224+
let msg = res.msg
225+
inc(msgCount)
226+
if msglist.contains(msg):
227+
msglist.delete(0)
228+
check true
229+
else:
230+
check false
229231
)
230232
# Check message received are correct (should be even integer in string format)
231233
var msglist2 = @["0", "2", "4", "6", "8"]
@@ -234,13 +236,15 @@ proc asyncpoll() =
234236
puller,
235237
ZMQ_POLLIN,
236238
proc(x: ZSocket) =
237-
let msg = x.receive()
238-
inc(msgCount2)
239-
if msglist2.contains(msg):
240-
msglist2.delete(0)
241-
check true
242-
else:
243-
check false
239+
let res = x.tryReceive()
240+
if res.msgAvailable:
241+
let msg = res.msg
242+
inc(msgCount2)
243+
if msglist2.contains(msg):
244+
msglist2.delete(0)
245+
check true
246+
else:
247+
check false
244248
)
245249

246250
let

zmq.nimble

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

3-
version = "1.5.1"
3+
version = "1.5.2"
44
author = "Andreas Rumpf"
55
description = "ZeroMQ wrapper"
66
license = "MIT"
@@ -18,4 +18,3 @@ task buildexamples, "Compile all examples":
1818

1919
task gendoc, "Generate documentation":
2020
exec("nim doc --mm:orc --project --out:docs/ zmq.nim")
21-

zmq/asynczmq.nim

+11-4
Original file line numberDiff line numberDiff line change
@@ -42,31 +42,39 @@ proc `=destroy`*(obj: AsyncZPoller) =
4242

4343
proc register*(poller: var AsyncZPoller, sock: ZSocket, event: int, cb: AsyncZPollCB) =
4444
## Register ZSocket function
45+
## The callback should ideally use non-blocking proc such ``waitForReceive`` or ``tryReceive`` or ``c.receive(DONTWAIT)``
4546
poller.zpoll.register(sock, event)
4647
poller.cb.add(cb)
4748

4849
proc register*(poller: var AsyncZPoller, conn: ZConnection, event: int, cb: AsyncZPollCB) =
4950
## Register ZConnection
51+
## The callback should ideally use non-blocking proc such ``waitForReceive`` or ``tryReceive`` or ``c.receive(DONTWAIT)``
5052
poller.register(conn.socket, event, cb)
5153

5254
proc register*(poller: var AsyncZPoller, item: ZPollItem, cb: AsyncZPollCB) =
53-
## Register ZConnection
55+
## Register ZConnection.
56+
## The callback should use non-blocking proc ``waitForReceive`` with strictly positive timeout or ``tryReceive`` or ``c.receive(DONTWAIT)``
5457
poller.zpoll.items.add(item)
5558
poller.cb.add(cb)
5659

5760
proc initZPoller*(poller: sink ZPoller, cb: AsyncZPollCB) : AsyncZPoller =
61+
## The callback should use non-blocking proc such ``waitForReceive`` or ``tryReceive`` or ``c.receive(DONTWAIT)``
5862
for p in poller.items:
5963
result.register(p, cb)
6064

6165
proc initZPoller*(args: openArray[tuple[item: ZConnection, cb: AsyncZPollCB]], event: cshort): AsyncZPoller =
6266
## Init a ZPoller with all items on the same event
67+
## The callback should use non-blocking proc ``waitForReceive`` with strictly positive timeout or ``tryReceive`` or ``c.receive(DONTWAIT)``
6368
for arg in args:
6469
result.register(arg.item, event, arg.cb)
6570

6671
proc pollAsync*(poller: AsyncZPoller, timeout: int = 2) : Future[int] =
6772
## Experimental API. Poll all the ZConnection and execute an async CB when ``event`` occurs.
73+
## The callback should use non-blocking proc ``waitForReceive`` with strictly positive timeout or ``tryReceive`` or ``c.receive(DONTWAIT)``
74+
75+
var timeout = max(2, timeout)
6876
result = newFuture[int]("pollAsync")
69-
var r = poller.zpoll.poll(timeout)
77+
var r = poller.zpoll.poll(timeout div 2)
7078
# ZMQ can't have a timeout smaller than one
7179
if r > 0:
7280
for i in 0..<poller.len():
@@ -78,7 +86,7 @@ proc pollAsync*(poller: AsyncZPoller, timeout: int = 2) : Future[int] =
7886

7987
if hasPendingOperations():
8088
# poll vs drain ?
81-
drain(timeout)
89+
drain(timeout div 2)
8290

8391
result.complete(r)
8492

@@ -158,4 +166,3 @@ proc sendAsync*(conn: ZConnection, msg: string, flags: ZSendRecvOptions = DONTWA
158166
# can send without blocking
159167
conn.send(msg, flags)
160168
fut.complete()
161-

zmq/connections.nim

+38-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ./bindings
2-
import std/[strformat]
2+
import std/[strformat, logging]
33

44
# Unofficial easier-for-Nim API
55

@@ -13,7 +13,7 @@ type
1313

1414
ZConnectionImpl* {.pure, final.} = object
1515
## A Zmq connection. Since ``ZContext`` and ``ZSocket`` are pointers, it is highly recommended to **not** copy ``ZConnection``.
16-
context*: ZContext ## Zmq context from C-bindings.
16+
context*: ZContext ## Zmq context from C-bindings.
1717
socket*: ZSocket ## Zmq socket from C-bindings.
1818
ownctx: bool # Boolean indicating if the connection owns the Zmq context
1919
alive: bool # Boolean indicating if the connection has been closed
@@ -32,16 +32,39 @@ proc zmqError*() {.noinline, noreturn.} =
3232
e.msg = &"Error: {e.error}. " & $strerror(e.error)
3333
raise e
3434

35+
var shouldLogEagainError = false
36+
37+
proc enableLogEagain*() =
38+
## Enable logging EAGAIN error in ZMQ calls
39+
shouldLogEagainError = true
40+
41+
proc disableLogEagain*() =
42+
## Disable logging EAGAIN error in ZMQ calls
43+
shouldLogEagainError = false
44+
3545
proc zmqErrorExceptEAGAIN() =
3646
var e: ref ZmqError
3747
new(e)
3848
e.error = errno()
49+
let errmsg = $strerror(e.error)
3950
if e.error == ZMQ_EAGAIN:
40-
discard
51+
if shouldLogEagainError:
52+
if logging.getHandlers().len() > 0:
53+
warn(errmsg)
54+
else:
55+
echo(errmsg)
56+
else:
57+
discard
4158
else:
42-
e.msg = &"Error: {e.error}. " & $strerror(e.error)
59+
e.msg = &"Error: {e.error}. " & errmsg
4360
raise e
4461

62+
template defaultFlag() : ZSendRecvOptions =
63+
when defined(defaultFlagDontWait):
64+
DONTWAIT
65+
else:
66+
NOFLAGS
67+
4568
#[
4669
# Context related proc
4770
]#
@@ -135,7 +158,6 @@ proc getsockopt*[T: SomeOrdinal|string](c: ZConnection, option: ZSockOptions): T
135158
## Check http://api.zeromq.org/4-2:zmq-setsockopt
136159
getsockopt[T](c.socket, option)
137160

138-
139161
#[
140162
Destructor
141163
]#
@@ -305,7 +327,7 @@ proc close*(c: ZConnection, linger: int = 500) =
305327

306328
# Send / Receive
307329
# Send with ZSocket type
308-
proc send*(s: ZSocket, msg: string, flags: ZSendRecvOptions = NOFLAGS) =
330+
proc send*(s: ZSocket, msg: string, flags: ZSendRecvOptions = defaultFlag()) =
309331
## Sends a message through the socket.
310332
var m: ZMsg
311333
if msg_init(m, msg.len) != 0:
@@ -330,7 +352,7 @@ proc sendAll*(s: ZSocket, msg: varargs[string]) =
330352
inc(i)
331353
s.send(msg[i])
332354

333-
proc send*(c: ZConnection, msg: string, flags: ZSendRecvOptions = NOFLAGS) =
355+
proc send*(c: ZConnection, msg: string, flags: ZSendRecvOptions = defaultFlag()) =
334356
## Sends a message over the connection.
335357
send(c.socket, msg, flags)
336358

@@ -339,7 +361,7 @@ proc sendAll*(c: ZConnection, msg: varargs[string]) =
339361
sendAll(c.socket, msg)
340362

341363
# receive with ZSocket type
342-
proc receiveImpl(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
364+
proc receiveImpl(s: ZSocket, flags: ZSendRecvOptions = defaultFlag()): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
343365
result.moreAvailable = false
344366
result.msgAvailable = false
345367

@@ -364,7 +386,7 @@ proc receiveImpl(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvaila
364386
if msg_close(m) != 0:
365387
zmqError()
366388

367-
proc waitForReceive*(s: ZSocket, timeout: int = -2, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
389+
proc waitForReceive*(s: ZSocket, timeout: int = -2, flags: ZSendRecvOptions = defaultFlag()): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
368390
## Set RCVTIMEO for the socket and wait until a message is available.
369391
## This function is blocking.
370392
##
@@ -392,7 +414,7 @@ proc waitForReceive*(s: ZSocket, timeout: int = -2, flags: ZSendRecvOptions = NO
392414
if shouldUpdateTimeout:
393415
s.setsockopt(RCVTIMEO, curtimeout.cint)
394416

395-
proc tryReceive*(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
417+
proc tryReceive*(s: ZSocket, flags: ZSendRecvOptions = defaultFlag()): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
396418
## Receives a message from a socket in a non-blocking way.
397419
##
398420
## Indicate whether a message was received or EAGAIN occured by ``msgAvailable``
@@ -406,13 +428,13 @@ proc tryReceive*(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvaila
406428
if (status and ZMQ_POLLIN) != 0:
407429
result = receiveImpl(s, flags)
408430

409-
proc receive*(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): string =
431+
proc receive*(s: ZSocket, flags: ZSendRecvOptions = defaultFlag()): string =
410432
## Receive a message on socket.
411433
#
412434
## Return an empty string on EAGAIN
413435
receiveImpl(s, flags).msg
414436

415-
proc receiveAll*(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): seq[string] =
437+
proc receiveAll*(s: ZSocket, flags: ZSendRecvOptions = defaultFlag()): seq[string] =
416438
## Receive all parts of a message
417439
##
418440
## If EAGAIN occurs without any data being received, it will be an empty seq
@@ -425,7 +447,7 @@ proc receiveAll*(s: ZSocket, flags: ZSendRecvOptions = NOFLAGS): seq[string] =
425447
else:
426448
expectMessage = false
427449

428-
proc waitForReceive*(c: ZConnection, timeout: int = -1, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
450+
proc waitForReceive*(c: ZConnection, timeout: int = -1, flags: ZSendRecvOptions = defaultFlag()): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
429451
## Set RCVTIMEO for the socket and wait until a message is available.
430452
## This function is blocking.
431453
##
@@ -438,19 +460,19 @@ proc waitForReceive*(c: ZConnection, timeout: int = -1, flags: ZSendRecvOptions
438460
## Indicate if more parts are needed to be received by ``moreAvailable``
439461
waitForReceive(c.socket, timeout, flags)
440462

441-
proc tryReceive*(c: ZConnection, flags: ZSendRecvOptions = NOFLAGS): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
463+
proc tryReceive*(c: ZConnection, flags: ZSendRecvOptions = defaultFlag()): tuple[msgAvailable: bool, moreAvailable: bool, msg: string] =
442464
## Receives a message from a socket in a non-blocking way.
443465
##
444466
## Indicate whether a message was received or EAGAIN occured by ``msgAvailable``
445467
##
446468
## Indicate if more parts are needed to be received by ``moreAvailable``
447469
tryReceive(c.socket, flags)
448470

449-
proc receive*(c: ZConnection, flags: ZSendRecvOptions = NOFLAGS): string =
471+
proc receive*(c: ZConnection, flags: ZSendRecvOptions = defaultFlag()): string =
450472
## Receive data over the connection
451473
receive(c.socket, flags)
452474

453-
proc receiveAll*(c: ZConnection, flags: ZSendRecvOptions = NOFLAGS): seq[string] =
475+
proc receiveAll*(c: ZConnection, flags: ZSendRecvOptions = defaultFlag()): seq[string] =
454476
## Receive all parts of a message
455477
##
456478
## If EAGAIN occurs without any data being received, it will be an empty seq

0 commit comments

Comments
 (0)