diff options
-rwxr-xr-x | build.bat | 6 | ||||
-rwxr-xr-x | config/nimrod.cfg | 3 | ||||
-rw-r--r-- | doc/lib.txt | 3 | ||||
-rwxr-xr-x | install.sh | 20 | ||||
-rwxr-xr-x | lib/core/macros.nim (renamed from lib/pure/macros.nim) | 0 | ||||
-rw-r--r-- | lib/core/marshal.nim | 259 | ||||
-rw-r--r-- | lib/core/threads.nim | 174 | ||||
-rwxr-xr-x | lib/nimbase.h | 9 | ||||
-rwxr-xr-x | lib/system.nim | 12 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 18 | ||||
-rwxr-xr-x | lib/system/gc.nim | 10 | ||||
-rwxr-xr-x | lib/system/systhread.nim | 79 | ||||
-rwxr-xr-x | rod/ccgstmts.nim | 4 | ||||
-rwxr-xr-x | rod/cgen.nim | 23 | ||||
-rw-r--r-- | tests/gc/tthreads.nim | 21 | ||||
-rwxr-xr-x | todo.txt | 6 | ||||
-rwxr-xr-x | web/news.txt | 1 |
17 files changed, 521 insertions, 127 deletions
diff --git a/build.bat b/build.bat index 29967f256..0bcaddd36 100755 --- a/build.bat +++ b/build.bat @@ -51,6 +51,8 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/idents.c -o build/1_1/idents.o %CC% %COMP_FLAGS% -Ibuild -c build/1_1/idents.c -o build/1_1/idents.o ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/astalgo.c -o build/1_1/astalgo.o %CC% %COMP_FLAGS% -Ibuild -c build/1_1/astalgo.c -o build/1_1/astalgo.o +ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/rodutils.c -o build/1_1/rodutils.o +%CC% %COMP_FLAGS% -Ibuild -c build/1_1/rodutils.c -o build/1_1/rodutils.o ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/extccomp.c -o build/1_1/extccomp.o %CC% %COMP_FLAGS% -Ibuild -c build/1_1/extccomp.c -o build/1_1/extccomp.o ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/osproc.c -o build/1_1/osproc.o @@ -146,8 +148,8 @@ ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/transf.c -o build/1_1/transf.o ECHO %CC% %COMP_FLAGS% -Ibuild -c build/1_1/parseopt.c -o build/1_1/parseopt.o %CC% %COMP_FLAGS% -Ibuild -c build/1_1/parseopt.c -o build/1_1/parseopt.o -ECHO %LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/interact.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o -%LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/interact.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o +ECHO %LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/interact.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o +%LINKER% %LINK_FLAGS% -o bin\nimrod.exe build/1_1/nim__dat.o build/1_1/system.o build/1_1/nimrod.o build/1_1/times.o build/1_1/strutils.o build/1_1/parseutils.o build/1_1/winlean.o build/1_1/commands.o build/1_1/os.o build/1_1/msgs.o build/1_1/options.o build/1_1/lists.o build/1_1/nstrtabs.o build/1_1/nhashes.o build/1_1/nversion.o build/1_1/condsyms.o build/1_1/ast.o build/1_1/crc.o build/1_1/ropes.o build/1_1/platform.o build/1_1/idents.o build/1_1/astalgo.o build/1_1/rodutils.o build/1_1/extccomp.o build/1_1/osproc.o build/1_1/strtabs.o build/1_1/hashes.o build/1_1/streams.o build/1_1/wordrecg.o build/1_1/scanner.o build/1_1/lexbase.o build/1_1/llstream.o build/1_1/nimconf.o build/1_1/main.o build/1_1/syntaxes.o build/1_1/pnimsyn.o build/1_1/pbraces.o build/1_1/ptmplsyn.o build/1_1/rnimsyn.o build/1_1/filters.o build/1_1/rodread.o build/1_1/rodwrite.o build/1_1/passes.o build/1_1/types.o build/1_1/trees.o build/1_1/math.o build/1_1/magicsys.o build/1_1/nimsets.o build/1_1/bitsets.o build/1_1/importer.o build/1_1/lookups.o build/1_1/semdata.o build/1_1/treetab.o build/1_1/sem.o build/1_1/evals.o build/1_1/semfold.o build/1_1/procfind.o build/1_1/pragmas.o build/1_1/docgen.o build/1_1/rst.o build/1_1/highlite.o build/1_1/cgen.o build/1_1/ccgutils.o build/1_1/cgmeth.o build/1_1/ecmasgen.o build/1_1/interact.o build/1_1/passaux.o build/1_1/depends.o build/1_1/transf.o build/1_1/parseopt.o ECHO SUCCESS diff --git a/config/nimrod.cfg b/config/nimrod.cfg index 122980348..4914d0993 100755 --- a/config/nimrod.cfg +++ b/config/nimrod.cfg @@ -1,5 +1,5 @@ # Configuration file for the Nimrod Compiler. -# (c) 2010 Andreas Rumpf +# (c) 2011 Andreas Rumpf # Feel free to edit the default values as you need. @@ -14,6 +14,7 @@ cc = gcc lib = "nimlib" @end +path="$lib/core" path="$lib/pure" path="$lib/impure" path="$lib/wrappers" diff --git a/doc/lib.txt b/doc/lib.txt index 47cd08cab..cefb1a1fe 100644 --- a/doc/lib.txt +++ b/doc/lib.txt @@ -31,6 +31,9 @@ Core implicitly by the compiler. Do not import it directly. It relies on compiler magic to work. +* `threads <threads.html>`_ + Nimrod thread support. + * `macros <macros.html>`_ Contains the AST API and documentation of Nimrod for writing macros. diff --git a/install.sh b/install.sh index 06f5c8a9c..9367ea95c 100755 --- a/install.sh +++ b/install.sh @@ -321,6 +321,8 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/system/sysstr.nim cp lib/system/systhread.nim $libdir/system/systhread.nim chmod 644 $libdir/system/systhread.nim + cp lib/pure/base64.nim $libdir/pure/base64.nim + chmod 644 $libdir/pure/base64.nim cp lib/pure/browsers.nim $libdir/pure/browsers.nim chmod 644 $libdir/pure/browsers.nim cp lib/pure/cgi.nim $libdir/pure/cgi.nim @@ -329,12 +331,12 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/pure/colors.nim cp lib/pure/complex.nim $libdir/pure/complex.nim chmod 644 $libdir/pure/complex.nim + cp lib/pure/cookies.nim $libdir/pure/cookies.nim + chmod 644 $libdir/pure/cookies.nim cp lib/pure/dynlib.nim $libdir/pure/dynlib.nim chmod 644 $libdir/pure/dynlib.nim cp lib/pure/hashes.nim $libdir/pure/hashes.nim chmod 644 $libdir/pure/hashes.nim - cp lib/pure/hashtabs.nim $libdir/pure/hashtabs.nim - chmod 644 $libdir/pure/hashtabs.nim cp lib/pure/htmlparser.nim $libdir/pure/htmlparser.nim chmod 644 $libdir/pure/htmlparser.nim cp lib/pure/httpclient.nim $libdir/pure/httpclient.nim @@ -347,8 +349,6 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/pure/lexbase.nim cp lib/pure/logging.nim $libdir/pure/logging.nim chmod 644 $libdir/pure/logging.nim - cp lib/pure/macros.nim $libdir/pure/macros.nim - chmod 644 $libdir/pure/macros.nim cp lib/pure/math.nim $libdir/pure/math.nim chmod 644 $libdir/pure/math.nim cp lib/pure/md5.nim $libdir/pure/md5.nim @@ -377,6 +377,10 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/pure/regexprs.nim cp lib/pure/ropes.nim $libdir/pure/ropes.nim chmod 644 $libdir/pure/ropes.nim + cp lib/pure/scgi.nim $libdir/pure/scgi.nim + chmod 644 $libdir/pure/scgi.nim + cp lib/pure/smtp.nim $libdir/pure/smtp.nim + chmod 644 $libdir/pure/smtp.nim cp lib/pure/sockets.nim $libdir/pure/sockets.nim chmod 644 $libdir/pure/sockets.nim cp lib/pure/streams.nim $libdir/pure/streams.nim @@ -391,8 +395,6 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/pure/times.nim cp lib/pure/unicode.nim $libdir/pure/unicode.nim chmod 644 $libdir/pure/unicode.nim - cp lib/pure/variants.nim $libdir/pure/variants.nim - chmod 644 $libdir/pure/variants.nim cp lib/pure/xmldom.nim $libdir/pure/xmldom.nim chmod 644 $libdir/pure/xmldom.nim cp lib/pure/xmldomparser.nim $libdir/pure/xmldomparser.nim @@ -421,10 +423,14 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/impure/osinfo_win.nim cp lib/impure/re.nim $libdir/impure/re.nim chmod 644 $libdir/impure/re.nim + cp lib/impure/ssl.nim $libdir/impure/ssl.nim + chmod 644 $libdir/impure/ssl.nim cp lib/impure/web.nim $libdir/impure/web.nim chmod 644 $libdir/impure/web.nim cp lib/impure/zipfiles.nim $libdir/impure/zipfiles.nim chmod 644 $libdir/impure/zipfiles.nim + cp lib/wrappers/claro.nim $libdir/wrappers/claro.nim + chmod 644 $libdir/wrappers/claro.nim cp lib/wrappers/expat.nim $libdir/wrappers/expat.nim chmod 644 $libdir/wrappers/expat.nim cp lib/wrappers/iup.nim $libdir/wrappers/iup.nim @@ -435,6 +441,8 @@ if [ $# -eq 1 ] ; then chmod 644 $libdir/wrappers/mysql.nim cp lib/wrappers/odbcsql.nim $libdir/wrappers/odbcsql.nim chmod 644 $libdir/wrappers/odbcsql.nim + cp lib/wrappers/openssl.nim $libdir/wrappers/openssl.nim + chmod 644 $libdir/wrappers/openssl.nim cp lib/wrappers/pcre.nim $libdir/wrappers/pcre.nim chmod 644 $libdir/wrappers/pcre.nim cp lib/wrappers/postgres.nim $libdir/wrappers/postgres.nim diff --git a/lib/pure/macros.nim b/lib/core/macros.nim index 7f5dda1e5..7f5dda1e5 100755 --- a/lib/pure/macros.nim +++ b/lib/core/macros.nim diff --git a/lib/core/marshal.nim b/lib/core/marshal.nim new file mode 100644 index 000000000..303b088f8 --- /dev/null +++ b/lib/core/marshal.nim @@ -0,0 +1,259 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains procs for serialization and deseralization of +## arbitrary Nimrod data structures. XXX This is not implemented yet! + +import streams + +proc load*[T](s: PStream, data: var T) {.magic: "Load".} + ## loads `data` from the stream `s`. Raises `EIO` in case of an error. + +proc store*[T](s: PStream, data: T) {.magic: "Store".} + ## stores `data` into the stream `s`. Raises `EIO` in case of an error. + + +proc reprInt(x: int64): string {.compilerproc.} = return $x +proc reprFloat(x: float): string {.compilerproc.} = return $x + +proc reprPointer(x: pointer): string {.compilerproc.} = + var buf: array [0..59, char] + c_sprintf(buf, "%p", x) + return $buf + +proc reprStrAux(result: var string, s: string) = + if cast[pointer](s) == nil: + add result, "nil" + return + add result, reprPointer(cast[pointer](s)) & "\"" + for c in items(s): + case c + of '"': add result, "\\\"" + of '\\': add result, "\\\\" # BUGFIX: forgotten + of '\10': add result, "\\10\"\n\"" # " \n " # better readability + of '\128' .. '\255', '\0'..'\9', '\11'..'\31': + add result, "\\" & reprInt(ord(c)) + else: result.add(c) + add result, "\"" + +proc reprStr(s: string): string {.compilerRtl.} = + result = "" + reprStrAux(result, s) + +proc reprBool(x: bool): string {.compilerRtl.} = + if x: result = "true" + else: result = "false" + +proc reprChar(x: char): string {.compilerRtl.} = + result = "\'" + case x + of '"': add result, "\\\"" + of '\\': add result, "\\\\" + of '\128' .. '\255', '\0'..'\31': add result, "\\" & reprInt(ord(x)) + else: add result, x + add result, "\'" + +proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = + if e <% typ.node.len: # BUGFIX + result = $typ.node.sons[e].name + else: + result = $e & " (invalid data!)" + +type + pbyteArray = ptr array[0.. 0xffff, byte] + +proc addSetElem(result: var string, elem: int, typ: PNimType) = + case typ.kind + of tyEnum: add result, reprEnum(elem, typ) + of tyBool: add result, reprBool(bool(elem)) + of tyChar: add result, reprChar(chr(elem)) + of tyRange: addSetElem(result, elem, typ.base) + of tyInt..tyInt64: add result, reprInt(elem) + else: # data corrupt --> inform the user + add result, " (invalid data!)" + +proc reprSetAux(result: var string, p: pointer, typ: PNimType) = + # "typ.slots.len" field is for sets the "first" field + var elemCounter = 0 # we need this flag for adding the comma at + # the right places + add result, "{" + var u: int64 + case typ.size + of 1: u = ze64(cast[ptr int8](p)^) + of 2: u = ze64(cast[ptr int16](p)^) + of 4: u = ze64(cast[ptr int32](p)^) + of 8: u = cast[ptr int64](p)^ + else: + var a = cast[pbyteArray](p) + for i in 0 .. typ.size*8-1: + if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0: + if elemCounter > 0: add result, ", " + addSetElem(result, i+typ.node.len, typ.base) + inc(elemCounter) + if typ.size <= 8: + for i in 0..sizeof(int64)*8-1: + if (u and (1 shl i)) != 0: + if elemCounter > 0: add result, ", " + addSetElem(result, i+typ.node.len, typ.base) + inc(elemCounter) + add result, "}" + +proc reprSet(p: pointer, typ: PNimType): string {.compilerRtl.} = + result = "" + reprSetAux(result, p, typ) + +type + TReprClosure {.final.} = object # we cannot use a global variable here + # as this wouldn't be thread-safe + marked: TCellSet + recdepth: int # do not recurse endless + indent: int # indentation + +when not defined(useNimRtl): + proc initReprClosure(cl: var TReprClosure) = + Init(cl.marked) + cl.recdepth = -1 # default is to display everything! + cl.indent = 0 + + proc deinitReprClosure(cl: var TReprClosure) = + Deinit(cl.marked) + + proc reprBreak(result: var string, cl: TReprClosure) = + add result, "\n" + for i in 0..cl.indent-1: add result, ' ' + + proc reprAux(result: var string, p: pointer, typ: PNimType, + cl: var TReprClosure) + + proc reprArray(result: var string, p: pointer, typ: PNimType, + cl: var TReprClosure) = + add result, "[" + var bs = typ.base.size + for i in 0..typ.size div bs - 1: + if i > 0: add result, ", " + reprAux(result, cast[pointer](cast[TAddress](p) + i*bs), typ.base, cl) + add result, "]" + + proc reprSequence(result: var string, p: pointer, typ: PNimType, + cl: var TReprClosure) = + if p == nil: + add result, "nil" + return + result.add(reprPointer(p) & "[") + var bs = typ.base.size + for i in 0..cast[PGenericSeq](p).len-1: + if i > 0: add result, ", " + reprAux(result, cast[pointer](cast[TAddress](p) + GenericSeqSize + i*bs), + typ.Base, cl) + add result, "]" + + proc reprRecordAux(result: var string, p: pointer, n: ptr TNimNode, + cl: var TReprClosure) = + case n.kind + of nkNone: assert(false) + of nkSlot: + add result, $n.name + add result, " = " + reprAux(result, cast[pointer](cast[TAddress](p) + n.offset), n.typ, cl) + of nkList: + for i in 0..n.len-1: + if i > 0: add result, ",\n" + reprRecordAux(result, p, n.sons[i], cl) + of nkCase: + var m = selectBranch(p, n) + reprAux(result, cast[pointer](cast[TAddress](p) + n.offset), n.typ, cl) + if m != nil: reprRecordAux(result, p, m, cl) + + proc reprRecord(result: var string, p: pointer, typ: PNimType, + cl: var TReprClosure) = + add result, "[" + reprRecordAux(result, p, typ.node, cl) + add result, "]" + + proc reprRef(result: var string, p: pointer, typ: PNimType, + cl: var TReprClosure) = + # we know that p is not nil here: + when defined(boehmGC) or defined(nogc): + var cell = cast[PCell](p) + else: + var cell = usrToCell(p) + add result, "ref " & reprPointer(p) + if cell notin cl.marked: + # only the address is shown: + incl(cl.marked, cell) + add result, " --> " + reprAux(result, p, typ.base, cl) + + proc reprAux(result: var string, p: pointer, typ: PNimType, + cl: var TReprClosure) = + if cl.recdepth == 0: + add result, "..." + return + dec(cl.recdepth) + case typ.kind + of tySet: reprSetAux(result, p, typ) + of tyArray: reprArray(result, p, typ, cl) + of tyTuple, tyPureObject: reprRecord(result, p, typ, cl) + of tyObject: + var t = cast[ptr PNimType](p)^ + reprRecord(result, p, t, cl) + of tyRef, tyPtr: + assert(p != nil) + if cast[ppointer](p)^ == nil: add result, "nil" + else: reprRef(result, cast[ppointer](p)^, typ, cl) + of tySequence: + reprSequence(result, cast[ppointer](p)^, typ, cl) + of tyInt: add result, $(cast[ptr int](p)^) + of tyInt8: add result, $int(cast[ptr Int8](p)^) + of tyInt16: add result, $int(cast[ptr Int16](p)^) + of tyInt32: add result, $int(cast[ptr Int32](p)^) + of tyInt64: add result, $(cast[ptr Int64](p)^) + of tyFloat: add result, $(cast[ptr float](p)^) + of tyFloat32: add result, $(cast[ptr float32](p)^) + of tyFloat64: add result, $(cast[ptr float64](p)^) + of tyEnum: add result, reprEnum(cast[ptr int](p)^, typ) + of tyBool: add result, reprBool(cast[ptr bool](p)^) + of tyChar: add result, reprChar(cast[ptr char](p)^) + of tyString: reprStrAux(result, cast[ptr string](p)^) + of tyCString: reprStrAux(result, $(cast[ptr cstring](p)^)) + of tyRange: reprAux(result, p, typ.base, cl) + of tyProc, tyPointer: + if cast[ppointer](p)^ == nil: add result, "nil" + else: add result, reprPointer(cast[ppointer](p)^) + else: + add result, "(invalid data!)" + inc(cl.recdepth) + +proc reprOpenArray(p: pointer, length: int, elemtyp: PNimType): string {. + compilerRtl.} = + var + cl: TReprClosure + initReprClosure(cl) + result = "[" + var bs = elemtyp.size + for i in 0..length - 1: + if i > 0: add result, ", " + reprAux(result, cast[pointer](cast[TAddress](p) + i*bs), elemtyp, cl) + add result, "]" + deinitReprClosure(cl) + +when not defined(useNimRtl): + proc reprAny(p: pointer, typ: PNimType): string = + var + cl: TReprClosure + initReprClosure(cl) + result = "" + if typ.kind in {tyObject, tyPureObject, tyTuple, tyArray, tySet}: + reprAux(result, p, typ, cl) + else: + var p = p + reprAux(result, addr(p), typ, cl) + add result, "\n" + deinitReprClosure(cl) + diff --git a/lib/core/threads.nim b/lib/core/threads.nim new file mode 100644 index 000000000..feb026547 --- /dev/null +++ b/lib/core/threads.nim @@ -0,0 +1,174 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Basic thread support for Nimrod. Note that Nimrod's default GC is still +## single-threaded. This means that either your threads should not allocate +## GC'ed memory, or you should compile with ``--gc:none`` or ``--gc:boehm``. +## +## Example: +## +## .. code-block:: nimrod +## +## var +## thr: array [0..4, TThread] +## L: TLock +## +## proc threadFunc(c: pointer) {.procvar.} = +## for i in 0..9: +## Aquire(L) # lock stdout +## echo i +## Release(L) +## +## InitLock(L) +## +## for i in 0..high(thr): +## createThread(thr[i], threadFunc) +## for i in 0..high(thr): +## joinThread(thr[i]) + + +# We jump through some hops here to ensure that Nimrod thread procs can have +# the Nimrod calling convention. This is needed because thread procs are +# ``stdcall`` on Windows and ``noconv`` on UNIX. Alternative would be to just +# use ``stdcall`` since it is mapped to ``noconv`` on UNIX anyway. However, +# the current approach will likely result in less problems later when we have +# GC'ed closures in Nimrod. + +type + TThreadProc* = proc (closure: pointer) ## Standard Nimrod thread proc. + TThreadProcClosure {.pure, final.} = object + fn: TThreadProc + data: pointer + +when defined(Windows): + type + THandle = int + TSysThread = THandle + TSysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi + DebugInfo: pointer + LockCount: int32 + RecursionCount: int32 + OwningThread: int + LockSemaphore: int + Reserved: int32 + + TWinThreadProc = proc (x: pointer): int32 {.stdcall.} + + TLock* = TSysLock ## Standard Nimrod Lock type. + + proc InitLock*(L: var TLock) {.stdcall, + dynlib: "kernel32", importc: "InitializeCriticalSection".} + ## Initializes the lock `L`. + + proc Aquire*(L: var TLock) {.stdcall, + dynlib: "kernel32", importc: "EnterCriticalSection".} + ## Aquires the lock `L`. + + proc Release*(L: var TLock) {.stdcall, + dynlib: "kernel32", importc: "LeaveCriticalSection".} + ## Releases the lock `L`. + + proc CreateThread(lpThreadAttributes: Pointer, dwStackSize: int32, + lpStartAddress: TWinThreadProc, + lpParameter: Pointer, + dwCreationFlags: int32, lpThreadId: var int32): THandle {. + stdcall, dynlib: "kernel32", importc: "CreateThread".} + + when false: + proc winSuspendThread(hThread: TSysThread): int32 {. + stdcall, dynlib: "kernel32", importc: "SuspendThread".} + + proc winResumeThread(hThread: TSysThread): int32 {. + stdcall, dynlib: "kernel32", importc: "ResumeThread".} + + proc WaitForMultipleObjects(nCount: int32, + lpHandles: ptr array[0..10, THandle], + bWaitAll: int32, + dwMilliseconds: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} + + proc WaitForSingleObject(hHandle: THANDLE, dwMilliseconds: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} + + proc TerminateThread(hThread: THandle, dwExitCode: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "TerminateThread".} + + proc threadProcWrapper(closure: pointer): int32 {.stdcall.} = + var c = cast[ptr TThreadProcClosure](closure) + c.fn(c.data) + # implicitely return 0 + +else: + type + TSysLock {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = int + TSysThread {.importc: "pthread_t", header: "<sys/types.h>".} = int + + TLock* = TSysLock + + proc InitLockAux(L: var TSysLock, attr: pointer = nil) {. + importc: "pthread_mutex_init", header: "<pthread.h>".} + + proc InitLock*(L: var TLock) {.inline.} = + InitLockAux(L) + proc Aquire*(L: var TLock) {. + importc: "pthread_mutex_lock", header: "<pthread.h>".} + proc Release*(L: var TLock) {. + importc: "pthread_mutex_unlock", header: "<pthread.h>".} + + proc pthread_create(a1: var TSysThread, a2: ptr int, + a3: proc (x: pointer) {.noconv.}, + a4: pointer): cint {.importc: "pthread_create", + header: "<pthread.h>".} + proc pthread_join(a1: TSysThread, a2: ptr pointer): cint {. + importc, header: "<pthread.h>".} + + proc pthread_cancel(a1: TSysThread): cint {. + importc: "pthread_cancel", header: "<pthread.h>".} + + proc threadProcWrapper(closure: pointer) {.noconv.} = + var c = cast[ptr TThreadProcClosure](closure) + c.fn(c.data) + + {.passL: "-pthread".} + {.passC: "-pthread".} + +type + TThread* = object of TObject ## Nimrod thread. + sys: TSysThread + c: TThreadProcClosure + + +proc createThread*(t: var TThread, tp: TThreadProc, + closure: pointer = nil) = + ## creates a new thread `t` and starts its execution. Entry point is the + ## proc `tp`. `closure` is passed to `tp`. + t.c.data = closure + t.c.fn = tp + when defined(windows): + var dummyThreadId: int32 + t.sys = CreateThread(nil, 0'i32, threadProcWrapper, addr(t.c), 0'i32, + dummyThreadId) + else: + discard pthread_create(t.sys, nil, threadProcWrapper, addr(t.c)) + +proc joinThread*(t: TThread) = + ## waits for the thread `t` until it has terminated. + when defined(windows): + discard WaitForSingleObject(t.sys, -1'i32) + else: + discard pthread_join(t.sys, nil) + +proc destroyThread*(t: var TThread) = + ## forces the thread `t` to terminate. This is potentially dangerous if + ## you don't have full control over `t` and its aquired ressources. + when defined(windows): + discard TerminateThread(t.sys, 1'i32) + else: + discard pthread_cancel(t.sys) + diff --git a/lib/nimbase.h b/lib/nimbase.h index 0251364d1..ab03b97fa 100755 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -71,7 +71,11 @@ __TINYC__ # define NIM_CONST const #endif -#define NIM_THREADVAR __thread +#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) +# define NIM_THREADVAR __declspec(thread) +#else +# define NIM_THREADVAR __thread +#endif /* --------------- how int64 constants should be declared: ----------- */ #if defined(__GNUC__) || defined(__LCC__) || \ @@ -415,8 +419,9 @@ struct TFrame { NI len; }; +/* extern TFrame* framePtr; -/*extern TSafePoint* excHandler; */ +extern TSafePoint* excHandler; */ #if defined(__cplusplus) struct NimException { diff --git a/lib/system.nim b/lib/system.nim index 262f0926d..e6ef4fa6f 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1259,6 +1259,18 @@ var ## each executed instruction. This should only be used by debuggers! ## Only code compiled with the ``debugger:on`` switch calls this hook. +type + PFrame = ptr TFrame + TFrame {.importc, nodecl, final.} = object + prev: PFrame + procname: CString + line: int # current line number + filename: CString + len: int # length of slots (when not debugging always zero) + +var + framePtr {.threadvar, compilerproc.}: PFrame + when not defined(ECMAScript): {.push overflow_checks:off} proc add* (x: var string, y: cstring) = diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 9beeb659e..4e8982691 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -1,7 +1,7 @@ # # # Nimrod's Runtime Library -# (c) Copyright 2010 Andreas Rumpf +# (c) Copyright 2011 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -40,10 +40,10 @@ type context: C_JmpBuf var - excHandler {.compilerproc.}: PSafePoint = nil + excHandler {.threadvar, compilerproc.}: PSafePoint = nil # list of exception handlers # a global variable for the root of all try blocks - currException: ref E_Base + currException {.threadvar.}: ref E_Base proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = s.prev = excHandler @@ -107,23 +107,11 @@ when nativeStacktrace: # interested in enabled = true -type - PFrame = ptr TFrame - TFrame {.importc, nodecl, final.} = object - prev: PFrame - procname: CString - line: int # current line number - filename: CString - len: int # length of slots (when not debugging always zero) - var buf: string # cannot be allocated on the stack! assertBuf: string # we need a different buffer for # assert, as it raises an exception and # exception handler needs the buffer too - - framePtr {.exportc.}: PFrame - tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! proc auxWriteStackTrace(f: PFrame, s: var string) = diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 72ae84096..c5bd56b99 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -61,8 +61,9 @@ type decStack: TCellSeq # cells in the stack that are to decref again cycleRoots: TCellSet tempStack: TCellSeq # temporary stack for recursion elimination - cycleRootsLock: TSysLock - zctLock: TSysLock + when hasThreadSupport: + cycleRootsLock: TSysLock + zctLock: TSysLock stat: TGcStat var @@ -281,8 +282,9 @@ proc initGC() = init(gch.tempStack) Init(gch.cycleRoots) Init(gch.decStack) - InitLock(gch.cycleRootsLock) - InitLock(gch.zctLock) + when hasThreadSupport: + InitLock(gch.cycleRootsLock) + InitLock(gch.zctLock) new(gOutOfMem) # reserve space for the EOutOfMemory exception here! proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = diff --git a/lib/system/systhread.nim b/lib/system/systhread.nim index b9c882ae0..0ffc9605c 100755 --- a/lib/system/systhread.nim +++ b/lib/system/systhread.nim @@ -44,83 +44,4 @@ proc atomicDec(memLoc: var int, x: int): int = else: dec(memLoc, x) result = memLoc - -when defined(Windows): - type - THandle = int - TSysThread = THandle - TSysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi - DebugInfo: pointer - LockCount: int32 - RecursionCount: int32 - OwningThread: int - LockSemaphore: int - Reserved: int32 - - proc InitLock(L: var TSysLock) {.stdcall, - dynlib: "kernel32", importc: "InitializeCriticalSection".} - proc Aquire(L: var TSysLock) {.stdcall, - dynlib: "kernel32", importc: "EnterCriticalSection".} - proc Release(L: var TSysLock) {.stdcall, - dynlib: "kernel32", importc: "LeaveCriticalSection".} - - proc CreateThread(lpThreadAttributes: Pointer, dwStackSize: int32, - lpStartAddress: pointer, lpParameter: Pointer, - dwCreationFlags: int32, lpThreadId: var int32): THandle {. - stdcall, dynlib: "kernel32", importc: "CreateThread".} - - proc winSuspendThread(hThread: TSysThread): int32 {. - stdcall, dynlib: "kernel32", importc: "SuspendThread".} - - proc winResumeThread(hThread: TSysThread): int32 {. - stdcall, dynlib: "kernel32", importc: "ResumeThread".} - - proc WaitForMultipleObjects(nCount: int32, - lpHandles: ptr array[0..10, THandle], - bWaitAll: int32, - dwMilliseconds: int32): int32 {. - stdcall, dynlib: "kernel32", importc: "WaitForMultipleObjects".} - - proc WaitForSingleObject(hHandle: THANDLE, dwMilliseconds: int32): int32 {. - stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} - -else: - type - TSysLock {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = int - TSysThread {.importc: "pthread_t", header: "<sys/types.h>".} = int - - proc InitLock(L: var TSysLock, attr: pointer = nil) {. - importc: "pthread_mutex_init", header: "<pthread.h>".} - proc Aquire(L: var TSysLock) {. - importc: "pthread_mutex_lock", header: "<pthread.h>".} - proc Release(L: var TSysLock) {. - importc: "pthread_mutex_unlock", header: "<pthread.h>".} - - proc pthread_create(a1: ptr TSysThread, a2: ptr int, - a3: proc (x: pointer): pointer {.noconv.}, - a4: pointer): cint {.importc: "pthread_create", - header: "<pthread.h>".} - proc pthread_join(a1: TSysThread, a2: ptr pointer): cint {. - importc, header: "<pthread.h>".} - - -type - TThread* = TSysThread - TLock* = TSysLock - TThreadFunc* = proc (closure: pointer) {.cdecl.} - -proc createThread*(t: var TThread, fn: TThreadFunc, closure: pointer) = - when defined(windows): - nil - else: - nil - #pthread_create( - -proc joinThread*(t: TThread) = - nil - -#proc pthread_exit(void *value_ptr) - -proc destroyThread*(t: var TThread) = - nil diff --git a/rod/ccgstmts.nim b/rod/ccgstmts.nim index f98a59580..2ac87824c 100755 --- a/rod/ccgstmts.nim +++ b/rod/ccgstmts.nim @@ -477,7 +477,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) = rethrowFlag = getTempName() appf(p.s[cpsLocals], "volatile NIM_BOOL $1 = NIM_FALSE;$n", [rethrowFlag]) if optStackTrace in p.Options: - app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl) + appcg(p, cpsStmts, "#framePtr = (TFrame*)&F;" & tnl) app(p.s[cpsStmts], "try {" & tnl) add(p.nestedTryStmts, t) genStmts(p, t.sons[0]) @@ -542,7 +542,7 @@ proc genTryStmt(p: BProc, t: PNode) = appcg(p, cpsStmts, "#pushSafePoint(&$1);$n" & "$1.status = setjmp($1.context);$n", [safePoint]) if optStackTrace in p.Options: - app(p.s[cpsStmts], "framePtr = (TFrame*)&F;" & tnl) + appcg(p, cpsStmts, "#framePtr = (TFrame*)&F;" & tnl) appf(p.s[cpsStmts], "if ($1.status == 0) {$n", [safePoint]) var length = sonsLen(t) add(p.nestedTryStmts, t) diff --git a/rod/cgen.nim b/rod/cgen.nim index 360b46a3b..f99a06a97 100755 --- a/rod/cgen.nim +++ b/rod/cgen.nim @@ -386,10 +386,10 @@ proc assignGlobalVar(p: BProc, s: PSym) = useHeader(p.module, s) if lfNoDecl in s.loc.flags: return if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ") + if sfThreadVar in s.flags: app(p.module.s[cfsVars], "NIM_THREADVAR ") app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t)) if sfRegister in s.flags: app(p.module.s[cfsVars], " register") if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile") - if sfThreadVar in s.flags: app(p.module.s[cfsVars], " NIM_THREADVAR") appf(p.module.s[cfsVars], " $1;$n", [s.loc.r]) if {optStackTrace, optEndb} * p.module.module.options == {optStackTrace, optEndb}: @@ -550,19 +550,12 @@ proc retIsNotVoid(s: PSym): bool = result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0]) proc initFrame(p: BProc, procname, filename: PRope): PRope = - inc(p.labels, 5) - result = ropeff("F.procname = $1;$n" & "F.prev = framePtr;$n" & - "F.filename = $2;$n" & "F.line = 0;$n" & "framePtr = (TFrame*)&F;$n", - "%LOC$3 = getelementptr %TF %F, %NI 1$n" & - "%LOC$4 = getelementptr %TF %F, %NI 0$n" & - "%LOC$5 = getelementptr %TF %F, %NI 3$n" & - "%LOC$6 = getelementptr %TF %F, %NI 2$n" & "store i8* $1, i8** %LOC$3$n" & - "store %TFrame* @framePtr, %TFrame** %LOC$4$n" & - "store i8* $2, i8** %LOC$5$n" & "store %NI 0, %NI* %LOC$6$n" & - "%LOC$7 = bitcast %TF* %F to %TFrame*$n" & - "store %TFrame* %LOC$7, %TFrame** @framePtr$n", [procname, filename, - toRope(p.labels), toRope(p.labels - 1), toRope(p.labels - 2), - toRope(p.labels - 3), toRope(p.labels - 4)]) + result = ropecg(p.module, + "F.procname = $1;$n" & + "F.prev = #framePtr;$n" & + "F.filename = $2;$n" & + "F.line = 0;$n" & + "framePtr = (TFrame*)&F;$n", [procname, filename]) proc deinitFrame(p: BProc): PRope = inc(p.labels, 3) @@ -693,10 +686,10 @@ proc genVarPrototype(m: BModule, sym: PSym) = [sym.loc.r, getTypeDesc(m, sym.loc.t)]) else: app(m.s[cfsVars], "extern ") + if sfThreadVar in sym.flags: app(m.s[cfsVars], "NIM_THREADVAR ") app(m.s[cfsVars], getTypeDesc(m, sym.loc.t)) if sfRegister in sym.flags: app(m.s[cfsVars], " register") if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile") - if sfThreadVar in sym.flags: app(m.s[cfsVars], " NIM_THREADVAR") appf(m.s[cfsVars], " $1;$n", [sym.loc.r]) proc genConstPrototype(m: BModule, sym: PSym) = diff --git a/tests/gc/tthreads.nim b/tests/gc/tthreads.nim new file mode 100644 index 000000000..2ef599e53 --- /dev/null +++ b/tests/gc/tthreads.nim @@ -0,0 +1,21 @@ + +import threads + +var + thr: array [0..4, TThread] + L: TLock + +proc threadFunc(c: pointer) {.procvar.} = + for i in 0..9: + Aquire(L) + echo i + Release(L) + +InitLock(L) + +for i in 0..high(thr): + createThread(thr[i], threadFunc) +for i in 0..high(thr): + joinThread(thr[i]) + + diff --git a/todo.txt b/todo.txt index 6a875ba9d..31fce261a 100755 --- a/todo.txt +++ b/todo.txt @@ -1,10 +1,14 @@ +- thread support: threadvar on Windows seems broken; + add --deadlock_prevention:on|off switch + - we need a way to disable tests - deprecate ^ and make it available as operator - test branch coverage - checked exceptions - built-in serialization -- do not ambiguity error for methods if amibiguity only affects the same +- do not ambiguity error for methods if ambiguity only affects the same dispatcher anyway +- slicing High priority (version 0.9.0) diff --git a/web/news.txt b/web/news.txt index f2ba9d521..e0c436a20 100755 --- a/web/news.txt +++ b/web/news.txt @@ -49,6 +49,7 @@ Additions ``array[TMyEnum, string]`` mapping. - Indices in array literals may be explicitly given, enhancing readability: ``[enumValueA: "a", enumValueB: "b"]``. +- Added basic thread support via the ``threads`` core module. |