basic thread support; still broken on Windows; untested on Mac OS X

This commit is contained in:
Araq 2011-01-18 02:22:01 +01:00
commit 66cfc851a1
17 changed files with 521 additions and 127 deletions

View file

@ -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 %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 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 %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 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 %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 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 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 %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 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/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 ECHO SUCCESS

View file

@ -1,5 +1,5 @@
# Configuration file for the Nimrod Compiler. # Configuration file for the Nimrod Compiler.
# (c) 2010 Andreas Rumpf # (c) 2011 Andreas Rumpf
# Feel free to edit the default values as you need. # Feel free to edit the default values as you need.
@ -14,6 +14,7 @@ cc = gcc
lib = "nimlib" lib = "nimlib"
@end @end
path="$lib/core"
path="$lib/pure" path="$lib/pure"
path="$lib/impure" path="$lib/impure"
path="$lib/wrappers" path="$lib/wrappers"

View file

@ -31,6 +31,9 @@ Core
implicitly by the compiler. Do not import it directly. It relies on compiler implicitly by the compiler. Do not import it directly. It relies on compiler
magic to work. magic to work.
* `threads <threads.html>`_
Nimrod thread support.
* `macros <macros.html>`_ * `macros <macros.html>`_
Contains the AST API and documentation of Nimrod for writing macros. Contains the AST API and documentation of Nimrod for writing macros.

View file

@ -321,6 +321,8 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/system/sysstr.nim chmod 644 $libdir/system/sysstr.nim
cp lib/system/systhread.nim $libdir/system/systhread.nim cp lib/system/systhread.nim $libdir/system/systhread.nim
chmod 644 $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 cp lib/pure/browsers.nim $libdir/pure/browsers.nim
chmod 644 $libdir/pure/browsers.nim chmod 644 $libdir/pure/browsers.nim
cp lib/pure/cgi.nim $libdir/pure/cgi.nim cp lib/pure/cgi.nim $libdir/pure/cgi.nim
@ -329,12 +331,12 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/pure/colors.nim chmod 644 $libdir/pure/colors.nim
cp lib/pure/complex.nim $libdir/pure/complex.nim cp lib/pure/complex.nim $libdir/pure/complex.nim
chmod 644 $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 cp lib/pure/dynlib.nim $libdir/pure/dynlib.nim
chmod 644 $libdir/pure/dynlib.nim chmod 644 $libdir/pure/dynlib.nim
cp lib/pure/hashes.nim $libdir/pure/hashes.nim cp lib/pure/hashes.nim $libdir/pure/hashes.nim
chmod 644 $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 cp lib/pure/htmlparser.nim $libdir/pure/htmlparser.nim
chmod 644 $libdir/pure/htmlparser.nim chmod 644 $libdir/pure/htmlparser.nim
cp lib/pure/httpclient.nim $libdir/pure/httpclient.nim cp lib/pure/httpclient.nim $libdir/pure/httpclient.nim
@ -347,8 +349,6 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/pure/lexbase.nim chmod 644 $libdir/pure/lexbase.nim
cp lib/pure/logging.nim $libdir/pure/logging.nim cp lib/pure/logging.nim $libdir/pure/logging.nim
chmod 644 $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 cp lib/pure/math.nim $libdir/pure/math.nim
chmod 644 $libdir/pure/math.nim chmod 644 $libdir/pure/math.nim
cp lib/pure/md5.nim $libdir/pure/md5.nim cp lib/pure/md5.nim $libdir/pure/md5.nim
@ -377,6 +377,10 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/pure/regexprs.nim chmod 644 $libdir/pure/regexprs.nim
cp lib/pure/ropes.nim $libdir/pure/ropes.nim cp lib/pure/ropes.nim $libdir/pure/ropes.nim
chmod 644 $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 cp lib/pure/sockets.nim $libdir/pure/sockets.nim
chmod 644 $libdir/pure/sockets.nim chmod 644 $libdir/pure/sockets.nim
cp lib/pure/streams.nim $libdir/pure/streams.nim cp lib/pure/streams.nim $libdir/pure/streams.nim
@ -391,8 +395,6 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/pure/times.nim chmod 644 $libdir/pure/times.nim
cp lib/pure/unicode.nim $libdir/pure/unicode.nim cp lib/pure/unicode.nim $libdir/pure/unicode.nim
chmod 644 $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 cp lib/pure/xmldom.nim $libdir/pure/xmldom.nim
chmod 644 $libdir/pure/xmldom.nim chmod 644 $libdir/pure/xmldom.nim
cp lib/pure/xmldomparser.nim $libdir/pure/xmldomparser.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 chmod 644 $libdir/impure/osinfo_win.nim
cp lib/impure/re.nim $libdir/impure/re.nim cp lib/impure/re.nim $libdir/impure/re.nim
chmod 644 $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 cp lib/impure/web.nim $libdir/impure/web.nim
chmod 644 $libdir/impure/web.nim chmod 644 $libdir/impure/web.nim
cp lib/impure/zipfiles.nim $libdir/impure/zipfiles.nim cp lib/impure/zipfiles.nim $libdir/impure/zipfiles.nim
chmod 644 $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 cp lib/wrappers/expat.nim $libdir/wrappers/expat.nim
chmod 644 $libdir/wrappers/expat.nim chmod 644 $libdir/wrappers/expat.nim
cp lib/wrappers/iup.nim $libdir/wrappers/iup.nim cp lib/wrappers/iup.nim $libdir/wrappers/iup.nim
@ -435,6 +441,8 @@ if [ $# -eq 1 ] ; then
chmod 644 $libdir/wrappers/mysql.nim chmod 644 $libdir/wrappers/mysql.nim
cp lib/wrappers/odbcsql.nim $libdir/wrappers/odbcsql.nim cp lib/wrappers/odbcsql.nim $libdir/wrappers/odbcsql.nim
chmod 644 $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 cp lib/wrappers/pcre.nim $libdir/wrappers/pcre.nim
chmod 644 $libdir/wrappers/pcre.nim chmod 644 $libdir/wrappers/pcre.nim
cp lib/wrappers/postgres.nim $libdir/wrappers/postgres.nim cp lib/wrappers/postgres.nim $libdir/wrappers/postgres.nim

259
lib/core/marshal.nim Normal file
View file

@ -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)

174
lib/core/threads.nim Normal file
View file

@ -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)

View file

@ -71,7 +71,11 @@ __TINYC__
# define NIM_CONST const # define NIM_CONST const
#endif #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: ----------- */ /* --------------- how int64 constants should be declared: ----------- */
#if defined(__GNUC__) || defined(__LCC__) || \ #if defined(__GNUC__) || defined(__LCC__) || \
@ -415,8 +419,9 @@ struct TFrame {
NI len; NI len;
}; };
/*
extern TFrame* framePtr; extern TFrame* framePtr;
/*extern TSafePoint* excHandler; */ extern TSafePoint* excHandler; */
#if defined(__cplusplus) #if defined(__cplusplus)
struct NimException { struct NimException {

View file

@ -1259,6 +1259,18 @@ var
## each executed instruction. This should only be used by debuggers! ## each executed instruction. This should only be used by debuggers!
## Only code compiled with the ``debugger:on`` switch calls this hook. ## 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): when not defined(ECMAScript):
{.push overflow_checks:off} {.push overflow_checks:off}
proc add* (x: var string, y: cstring) = proc add* (x: var string, y: cstring) =

View file

@ -1,7 +1,7 @@
# #
# #
# Nimrod's Runtime Library # Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -40,10 +40,10 @@ type
context: C_JmpBuf context: C_JmpBuf
var var
excHandler {.compilerproc.}: PSafePoint = nil excHandler {.threadvar, compilerproc.}: PSafePoint = nil
# list of exception handlers # list of exception handlers
# a global variable for the root of all try blocks # 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.} = proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
s.prev = excHandler s.prev = excHandler
@ -107,23 +107,11 @@ when nativeStacktrace:
# interested in # interested in
enabled = true 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 var
buf: string # cannot be allocated on the stack! buf: string # cannot be allocated on the stack!
assertBuf: string # we need a different buffer for assertBuf: string # we need a different buffer for
# assert, as it raises an exception and # assert, as it raises an exception and
# exception handler needs the buffer too # exception handler needs the buffer too
framePtr {.exportc.}: PFrame
tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! tempFrames: array [0..127, PFrame] # cannot be allocated on the stack!
proc auxWriteStackTrace(f: PFrame, s: var string) = proc auxWriteStackTrace(f: PFrame, s: var string) =

View file

@ -61,8 +61,9 @@ type
decStack: TCellSeq # cells in the stack that are to decref again decStack: TCellSeq # cells in the stack that are to decref again
cycleRoots: TCellSet cycleRoots: TCellSet
tempStack: TCellSeq # temporary stack for recursion elimination tempStack: TCellSeq # temporary stack for recursion elimination
cycleRootsLock: TSysLock when hasThreadSupport:
zctLock: TSysLock cycleRootsLock: TSysLock
zctLock: TSysLock
stat: TGcStat stat: TGcStat
var var
@ -281,8 +282,9 @@ proc initGC() =
init(gch.tempStack) init(gch.tempStack)
Init(gch.cycleRoots) Init(gch.cycleRoots)
Init(gch.decStack) Init(gch.decStack)
InitLock(gch.cycleRootsLock) when hasThreadSupport:
InitLock(gch.zctLock) InitLock(gch.cycleRootsLock)
InitLock(gch.zctLock)
new(gOutOfMem) # reserve space for the EOutOfMemory exception here! new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =

View file

@ -44,83 +44,4 @@ proc atomicDec(memLoc: var int, x: int): int =
else: else:
dec(memLoc, x) dec(memLoc, x)
result = memLoc 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

View file

@ -477,7 +477,7 @@ proc genTryStmtCpp(p: BProc, t: PNode) =
rethrowFlag = getTempName() rethrowFlag = getTempName()
appf(p.s[cpsLocals], "volatile NIM_BOOL $1 = NIM_FALSE;$n", [rethrowFlag]) appf(p.s[cpsLocals], "volatile NIM_BOOL $1 = NIM_FALSE;$n", [rethrowFlag])
if optStackTrace in p.Options: 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) app(p.s[cpsStmts], "try {" & tnl)
add(p.nestedTryStmts, t) add(p.nestedTryStmts, t)
genStmts(p, t.sons[0]) genStmts(p, t.sons[0])
@ -542,7 +542,7 @@ proc genTryStmt(p: BProc, t: PNode) =
appcg(p, cpsStmts, "#pushSafePoint(&$1);$n" & appcg(p, cpsStmts, "#pushSafePoint(&$1);$n" &
"$1.status = setjmp($1.context);$n", [safePoint]) "$1.status = setjmp($1.context);$n", [safePoint])
if optStackTrace in p.Options: 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]) appf(p.s[cpsStmts], "if ($1.status == 0) {$n", [safePoint])
var length = sonsLen(t) var length = sonsLen(t)
add(p.nestedTryStmts, t) add(p.nestedTryStmts, t)

View file

@ -386,10 +386,10 @@ proc assignGlobalVar(p: BProc, s: PSym) =
useHeader(p.module, s) useHeader(p.module, s)
if lfNoDecl in s.loc.flags: return if lfNoDecl in s.loc.flags: return
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ") 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)) app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register") if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile") 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]) appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
if {optStackTrace, optEndb} * p.module.module.options == if {optStackTrace, optEndb} * p.module.module.options ==
{optStackTrace, optEndb}: {optStackTrace, optEndb}:
@ -550,19 +550,12 @@ proc retIsNotVoid(s: PSym): bool =
result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0]) result = (s.typ.sons[0] != nil) and not isInvalidReturnType(s.typ.sons[0])
proc initFrame(p: BProc, procname, filename: PRope): PRope = proc initFrame(p: BProc, procname, filename: PRope): PRope =
inc(p.labels, 5) result = ropecg(p.module,
result = ropeff("F.procname = $1;$n" & "F.prev = framePtr;$n" & "F.procname = $1;$n" &
"F.filename = $2;$n" & "F.line = 0;$n" & "framePtr = (TFrame*)&F;$n", "F.prev = #framePtr;$n" &
"%LOC$3 = getelementptr %TF %F, %NI 1$n" & "F.filename = $2;$n" &
"%LOC$4 = getelementptr %TF %F, %NI 0$n" & "F.line = 0;$n" &
"%LOC$5 = getelementptr %TF %F, %NI 3$n" & "framePtr = (TFrame*)&F;$n", [procname, filename])
"%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)])
proc deinitFrame(p: BProc): PRope = proc deinitFrame(p: BProc): PRope =
inc(p.labels, 3) inc(p.labels, 3)
@ -693,10 +686,10 @@ proc genVarPrototype(m: BModule, sym: PSym) =
[sym.loc.r, getTypeDesc(m, sym.loc.t)]) [sym.loc.r, getTypeDesc(m, sym.loc.t)])
else: else:
app(m.s[cfsVars], "extern ") 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)) app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if sfRegister in sym.flags: app(m.s[cfsVars], " register") if sfRegister in sym.flags: app(m.s[cfsVars], " register")
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile") 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]) appf(m.s[cfsVars], " $1;$n", [sym.loc.r])
proc genConstPrototype(m: BModule, sym: PSym) = proc genConstPrototype(m: BModule, sym: PSym) =

21
tests/gc/tthreads.nim Normal file
View file

@ -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])

View file

@ -1,10 +1,14 @@
- thread support: threadvar on Windows seems broken;
add --deadlock_prevention:on|off switch
- we need a way to disable tests - we need a way to disable tests
- deprecate ^ and make it available as operator - deprecate ^ and make it available as operator
- test branch coverage - test branch coverage
- checked exceptions - checked exceptions
- built-in serialization - 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 dispatcher anyway
- slicing
High priority (version 0.9.0) High priority (version 0.9.0)

View file

@ -49,6 +49,7 @@ Additions
``array[TMyEnum, string]`` mapping. ``array[TMyEnum, string]`` mapping.
- Indices in array literals may be explicitly given, enhancing readability: - Indices in array literals may be explicitly given, enhancing readability:
``[enumValueA: "a", enumValueB: "b"]``. ``[enumValueA: "a", enumValueB: "b"]``.
- Added basic thread support via the ``threads`` core module.