basic message passing working

This commit is contained in:
Araq 2011-07-09 01:18:33 +02:00
commit 2565ff8dde
4 changed files with 56 additions and 17 deletions

View file

@ -236,7 +236,7 @@ type
EInvalidObjectAssignment* = EInvalidObjectAssignment* =
object of ESynch ## is raised if an object gets assigned to its object of ESynch ## is raised if an object gets assigned to its
## farther's object. ## parent's object.
EInvalidObjectConversion* = EInvalidObjectConversion* =
object of ESynch ## is raised if an object is converted to an incompatible object of ESynch ## is raised if an object is converted to an incompatible
@ -261,6 +261,9 @@ type
## that cannot be represented with infinite ## that cannot be represented with infinite
## precision -- for example, 2.0 / 3.0, log(1.1) ## precision -- for example, 2.0 / 3.0, log(1.1)
## NOTE: Nimrod currently does not detect these! ## NOTE: Nimrod currently does not detect these!
EDeadThread* =
object of ESynch ## is raised if it is attempted to send a message to a
## dead thread.
TResult* = enum Failure, Success TResult* = enum Failure, Success

View file

@ -22,6 +22,8 @@ type
PInbox = ptr TInbox PInbox = ptr TInbox
TLoadStoreMode = enum mStore, mLoad TLoadStoreMode = enum mStore, mLoad
const ThreadDeadMask = -2
proc initInbox(p: pointer) = proc initInbox(p: pointer) =
var inbox = cast[PInbox](p) var inbox = cast[PInbox](p)
initSysLock(inbox.lock) initSysLock(inbox.lock)
@ -30,6 +32,9 @@ proc initInbox(p: pointer) =
proc freeInbox(p: pointer) = proc freeInbox(p: pointer) =
var inbox = cast[PInbox](p) var inbox = cast[PInbox](p)
# we need to grab the lock to be save against sending threads!
acquireSys(inbox.lock)
inbox.mask = ThreadDeadMask
deallocOsPages(inbox.region) deallocOsPages(inbox.region)
deinitSys(inbox.lock) deinitSys(inbox.lock)
deinitSysCond(inbox.cond) deinitSysCond(inbox.cond)
@ -158,7 +163,6 @@ proc rawSend(q: PInbox, data: pointer, typ: PNimType) =
q.mask = cap*2 - 1 q.mask = cap*2 - 1
q.wr = q.count q.wr = q.count
q.rd = 0 q.rd = 0
#echo "came here"
storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore) storeAux(addr(q.data[q.wr * typ.size]), data, typ, q, mStore)
inc q.count inc q.count
q.wr = (q.wr + 1) and q.mask q.wr = (q.wr + 1) and q.mask
@ -177,23 +181,34 @@ template lockInbox(q: expr, action: stmt) =
proc send*[TMsg](receiver: var TThread[TMsg], msg: TMsg) = proc send*[TMsg](receiver: var TThread[TMsg], msg: TMsg) =
## sends a message to a thread. `msg` is deeply copied. ## sends a message to a thread. `msg` is deeply copied.
var q = cast[PInbox](getInBoxMem(receiver)) var q = cast[PInbox](getInBoxMem(receiver))
if q.mask == ThreadDeadMask:
raise newException(EDeadThread, "cannot send message; thread died")
acquireSys(q.lock) acquireSys(q.lock)
var m: TMsg var m: TMsg
shallowCopy(m, msg) shallowCopy(m, msg)
rawSend(q, addr(m), cast[PNimType](getTypeInfo(msg))) var typ = cast[PNimType](getTypeInfo(msg))
rawSend(q, addr(m), typ)
q.elemType = typ
releaseSys(q.lock) releaseSys(q.lock)
SignalSysCond(q.cond) SignalSysCond(q.cond)
proc recv*[TMsg](): TMsg = proc llRecv(res: pointer, typ: PNimType) =
## receives a message from its internal message queue. This blocks until # to save space, the generic is as small as possible
## a message has arrived! You may use ``peek`` to avoid the blocking.
var q = cast[PInbox](getInBoxMem()) var q = cast[PInbox](getInBoxMem())
acquireSys(q.lock) acquireSys(q.lock)
while q.count <= 0: while q.count <= 0:
WaitSysCond(q.cond, q.lock) WaitSysCond(q.cond, q.lock)
rawRecv(q, addr(result), cast[PNimType](getTypeInfo(result))) if typ != q.elemType:
releaseSys(q.lock)
raise newException(EInvalidValue, "cannot receive message of wrong type")
rawRecv(q, res, typ)
releaseSys(q.lock) releaseSys(q.lock)
proc recv*[TMsg](): TMsg =
## receives a message from its internal message queue. This blocks until
## a message has arrived! You may use ``peek`` to avoid the blocking.
llRecv(addr(result), cast[PNimType](getTypeInfo(result)))
proc peek*(): int = proc peek*(): int =
## returns the current number of messages in the inbox. ## returns the current number of messages in the inbox.
var q = cast[PInbox](getInBoxMem()) var q = cast[PInbox](getInBoxMem())

View file

@ -249,7 +249,8 @@ when not defined(useNimRtl):
type type
TThread* {.pure, final.}[TParam] = object of TGcThread ## Nimrod thread. TThread* {.pure, final.}[TParam] = object of TGcThread ## Nimrod thread.
fn: proc (p: TParam) emptyFn: proc ()
dataFn: proc (p: TParam)
data: TParam data: TParam
proc initInbox(p: pointer) proc initInbox(p: pointer)
@ -268,14 +269,14 @@ template ThreadProcWrapperBody(closure: expr) =
initGC() initGC()
t.stackBottom = addr(t) t.stackBottom = addr(t)
registerThread(t) registerThread(t)
initInbox(addr(t.inbox))
try: try:
when false: when false:
var a = addr(tls) var a = addr(tls)
var b = MaskStackPointer(1293920-372736-303104-36864) var b = MaskStackPointer(1293920-372736-303104-36864)
c_fprintf(c_stdout, "TLS: %p\nmasked: %p\ndiff: %ld\n", c_fprintf(c_stdout, "TLS: %p\nmasked: %p\ndiff: %ld\n",
a, b, cast[int](a) - cast[int](b)) a, b, cast[int](a) - cast[int](b))
t.fn(t.data) if t.emptyFn == nil: t.dataFn(t.data)
else: t.emptyFn()
finally: finally:
# XXX shut-down is not executed when the thread is forced down! # XXX shut-down is not executed when the thread is forced down!
freeInbox(addr(t.inbox)) freeInbox(addr(t.inbox))
@ -326,8 +327,28 @@ proc createThread*[TParam](t: var TThread[TParam],
## creates a new thread `t` and starts its execution. Entry point is the ## creates a new thread `t` and starts its execution. Entry point is the
## proc `tp`. `param` is passed to `tp`. ## proc `tp`. `param` is passed to `tp`.
t.data = param t.data = param
t.fn = tp t.dataFn = tp
t.stackSize = ThreadStackSize t.stackSize = ThreadStackSize
initInbox(addr(t.inbox))
when hostOS == "windows":
var dummyThreadId: int32
t.sys = CreateThread(nil, ThreadStackSize, threadProcWrapper[TParam],
addr(t), 0'i32, dummyThreadId)
if t.sys <= 0:
raise newException(EResourceExhausted, "cannot create thread")
else:
var a: Tpthread_attr
pthread_attr_init(a)
pthread_attr_setstacksize(a, ThreadStackSize)
if pthread_create(t.sys, a, threadProcWrapper[TParam], addr(t)) != 0:
raise newException(EResourceExhausted, "cannot create thread")
proc createThread*[TParam](t: var TThread[TParam], tp: proc () {.thread.}) =
## creates a new thread `t` and starts its execution. Entry point is the
## proc `tp`.
t.emptyFn = tp
t.stackSize = ThreadStackSize
initInbox(addr(t.inbox))
when hostOS == "windows": when hostOS == "windows":
var dummyThreadId: int32 var dummyThreadId: int32
t.sys = CreateThread(nil, ThreadStackSize, threadProcWrapper[TParam], t.sys = CreateThread(nil, ThreadStackSize, threadProcWrapper[TParam],
@ -342,9 +363,9 @@ proc createThread*[TParam](t: var TThread[TParam],
raise newException(EResourceExhausted, "cannot create thread") raise newException(EResourceExhausted, "cannot create thread")
when useStackMaskHack: when useStackMaskHack:
proc runMain(tp: proc (dummy: pointer) {.thread.}) {.compilerproc.} = proc runMain(tp: proc () {.thread.}) {.compilerproc.} =
var mainThread: TThread[pointer] var mainThread: TThread[pointer]
createThread(mainThread, tp, nil) createThread(mainThread, tp)
joinThread(mainThread) joinThread(mainThread)
# --------------------------- lock handling ---------------------------------- # --------------------------- lock handling ----------------------------------
@ -462,9 +483,9 @@ proc Release*(lock: var TLock) =
# ------------------------ message passing support --------------------------- # ------------------------ message passing support ---------------------------
proc getInBoxMem*[TMsg](t: var TThread[TMsg]): pointer {.inline.} = proc getInBoxMem[TMsg](t: var TThread[TMsg]): pointer {.inline.} =
result = addr(t.inbox) result = addr(t.inbox)
proc getInBoxMem*(): pointer {.inline.} = proc getInBoxMem(): pointer {.inline.} =
result = addr(cast[PGcThread](ThreadVarGetValue(globalsSlot)).inbox) result = addr(cast[PGcThread](ThreadVarGetValue(globalsSlot)).inbox)

View file

@ -1,12 +1,12 @@
High priority (version 0.8.12) High priority (version 0.8.12)
============================== ==============================
* ``force_nosideEffect`` or some similar pragma is needed (``loophole``?)
* test threads on windows * test threads on windows
* test thread analysis: * test thread analysis:
var x = globalString # ok, copied; `x` is mine! var x = globalString # ok, copied; `x` is mine!
vs vs
var x = globalRef # read access, `x` is theirs! var x = globalRef # read access, `x` is theirs!
* test message passing built-ins
* make threadvar efficient again on linux after testing * make threadvar efficient again on linux after testing
* document Nimrod's threads * document Nimrod's threads
* document Nimrod's two phase symbol lookup for generics * document Nimrod's two phase symbol lookup for generics