mirror of
https://github.com/nim-lang/Nim.git
synced 2026-02-13 06:43:52 +00:00
made large parts of the stdlib gcsafe
This commit is contained in:
@@ -118,7 +118,7 @@ type
|
||||
warnNilStatement, warnAnalysisLoophole,
|
||||
warnDifferentHeaps, warnWriteToForeignHeap, warnImplicitClosure,
|
||||
warnEachIdentIsTuple, warnShadowIdent,
|
||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe,
|
||||
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2,
|
||||
warnUninit, warnGcMem, warnUser,
|
||||
hintSuccess, hintSuccessX,
|
||||
hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded,
|
||||
@@ -387,6 +387,7 @@ const
|
||||
warnProveField: "cannot prove that field '$1' is accessible [ProveField]",
|
||||
warnProveIndex: "cannot prove index '$1' is valid [ProveIndex]",
|
||||
warnGcUnsafe: "not GC-safe: '$1' [GcUnsafe]",
|
||||
warnGcUnsafe2: "cannot prove '$1' is GC-safe. This will become a compile time error in the future.",
|
||||
warnUninit: "'$1' might not have been initialized [Uninit]",
|
||||
warnGcMem: "'$1' uses GC'ed memory [GcMem]",
|
||||
warnUser: "$1 [User]",
|
||||
@@ -408,7 +409,7 @@ const
|
||||
hintUser: "$1 [User]"]
|
||||
|
||||
const
|
||||
WarningsToStr*: array[0..25, string] = ["CannotOpenFile", "OctalEscape",
|
||||
WarningsToStr*: array[0..26, string] = ["CannotOpenFile", "OctalEscape",
|
||||
"XIsNeverRead", "XmightNotBeenInit",
|
||||
"Deprecated", "ConfigDeprecated",
|
||||
"SmallLshouldNotBeUsed", "UnknownMagic",
|
||||
@@ -416,7 +417,7 @@ const
|
||||
"CommentXIgnored", "NilStmt",
|
||||
"AnalysisLoophole", "DifferentHeaps", "WriteToForeignHeap",
|
||||
"ImplicitClosure", "EachIdentIsTuple", "ShadowIdent",
|
||||
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "Uninit",
|
||||
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit",
|
||||
"GcMem", "User"]
|
||||
|
||||
HintsToStr*: array[0..15, string] = ["Success", "SuccessX", "LineTooLong",
|
||||
|
||||
@@ -53,7 +53,7 @@ const
|
||||
wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
|
||||
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
|
||||
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
|
||||
wBorrow}
|
||||
wBorrow, wGcSafe}
|
||||
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
|
||||
wImportCpp, wImportObjC, wError}
|
||||
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
|
||||
@@ -690,7 +690,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfThread)
|
||||
of wGcSafe:
|
||||
noVal(it)
|
||||
incl(sym.flags, sfThread)
|
||||
if sym.kind != skType: incl(sym.flags, sfThread)
|
||||
if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
|
||||
else: invalidPragma(it)
|
||||
of wPacked:
|
||||
|
||||
@@ -113,7 +113,8 @@ proc useVar(a: PEffects, n: PNode) =
|
||||
if {sfGlobal, sfThread} * s.flags == {sfGlobal} and s.kind == skVar:
|
||||
when trackGlobals:
|
||||
a.addUse(copyNode(n))
|
||||
if tfHasGCedMem in s.typ.flags or s.typ.isGCedMem:
|
||||
if (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem) and
|
||||
tfGcSafe notin s.typ.flags:
|
||||
message(n.info, warnGcUnsafe, renderTree(n))
|
||||
a.gcUnsafe = true
|
||||
|
||||
@@ -517,6 +518,7 @@ proc track(tracked: PEffects, n: PNode) =
|
||||
addEffect(tracked, createRaise(n))
|
||||
addTag(tracked, createTag(n))
|
||||
when trackGlobals: addUse(tracked, createAnyGlobal(n))
|
||||
# XXX handle 'gcsafe' properly for callbacks!
|
||||
else:
|
||||
mergeEffects(tracked, effectList.sons[exceptionEffects], n)
|
||||
mergeTags(tracked, effectList.sons[tagEffects], n)
|
||||
@@ -704,7 +706,8 @@ proc trackProc*(s: PSym, body: PNode) =
|
||||
"uses an unlisted global variable: ", hints=on, symbolPredicate)
|
||||
effects.sons[usesEffects] = usesSpec
|
||||
if sfThread in s.flags and t.gcUnsafe:
|
||||
localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
||||
localError(s.info, warnGcUnsafe2, s.name.s)
|
||||
#localError(s.info, "'$1' is not GC-safe" % s.name.s)
|
||||
if not t.gcUnsafe: s.typ.flags.incl tfGcSafe
|
||||
|
||||
proc trackTopLevelStmt*(module: PSym; n: PNode) =
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
## This module contains the interface to the compiler's abstract syntax
|
||||
## tree (`AST`:idx:). Macros operate on this tree.
|
||||
@@ -109,19 +110,20 @@ const
|
||||
nnkCallKinds* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand,
|
||||
nnkCallStrLit}
|
||||
|
||||
proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild".}
|
||||
proc `[]`*(n: PNimrodNode, i: int): PNimrodNode {.magic: "NChild", noSideEffect.}
|
||||
## get `n`'s `i`'th child.
|
||||
|
||||
proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild".}
|
||||
proc `[]=`*(n: PNimrodNode, i: int, child: PNimrodNode) {.magic: "NSetChild",
|
||||
noSideEffect.}
|
||||
## set `n`'s `i`'th child to `child`.
|
||||
|
||||
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent".}
|
||||
proc `!`*(s: string): TNimrodIdent {.magic: "StrToIdent", noSideEffect.}
|
||||
## constructs an identifier from the string `s`
|
||||
|
||||
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr".}
|
||||
proc `$`*(i: TNimrodIdent): string {.magic: "IdentToStr", noSideEffect.}
|
||||
## converts a Nimrod identifier to a string
|
||||
|
||||
proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr".}
|
||||
proc `$`*(s: PNimrodSymbol): string {.magic: "IdentToStr", noSideEffect.}
|
||||
## converts a Nimrod symbol to a string
|
||||
|
||||
proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
|
||||
@@ -130,35 +132,36 @@ proc `==`*(a, b: TNimrodIdent): bool {.magic: "EqIdent", noSideEffect.}
|
||||
proc `==`*(a, b: PNimrodNode): bool {.magic: "EqNimrodNode", noSideEffect.}
|
||||
## compares two Nimrod nodes
|
||||
|
||||
proc len*(n: PNimrodNode): int {.magic: "NLen".}
|
||||
proc len*(n: PNimrodNode): int {.magic: "NLen", noSideEffect.}
|
||||
## returns the number of children of `n`.
|
||||
|
||||
proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable.}
|
||||
proc add*(father, child: PNimrodNode): PNimrodNode {.magic: "NAdd", discardable,
|
||||
noSideEffect.}
|
||||
## Adds the `child` to the `father` node. Returns the
|
||||
## father node so that calls can be nested.
|
||||
|
||||
proc add*(father: PNimrodNode, children: varargs[PNimrodNode]): PNimrodNode {.
|
||||
magic: "NAddMultiple", discardable.}
|
||||
magic: "NAddMultiple", discardable, noSideEffect.}
|
||||
## Adds each child of `children` to the `father` node.
|
||||
## Returns the `father` node so that calls can be nested.
|
||||
|
||||
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel".}
|
||||
proc del*(father: PNimrodNode, idx = 0, n = 1) {.magic: "NDel", noSideEffect.}
|
||||
## deletes `n` children of `father` starting at index `idx`.
|
||||
|
||||
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind".}
|
||||
proc kind*(n: PNimrodNode): TNimrodNodeKind {.magic: "NKind", noSideEffect.}
|
||||
## returns the `kind` of the node `n`.
|
||||
|
||||
proc intVal*(n: PNimrodNode): BiggestInt {.magic: "NIntVal".}
|
||||
proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal".}
|
||||
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol".}
|
||||
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent".}
|
||||
proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType".}
|
||||
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal".}
|
||||
proc intVal*(n: PNimrodNode): BiggestInt {.magic: "NIntVal", noSideEffect.}
|
||||
proc floatVal*(n: PNimrodNode): BiggestFloat {.magic: "NFloatVal", noSideEffect.}
|
||||
proc symbol*(n: PNimrodNode): PNimrodSymbol {.magic: "NSymbol", noSideEffect.}
|
||||
proc ident*(n: PNimrodNode): TNimrodIdent {.magic: "NIdent", noSideEffect.}
|
||||
proc typ*(n: PNimrodNode): typedesc {.magic: "NGetType", noSideEffect.}
|
||||
proc strVal*(n: PNimrodNode): string {.magic: "NStrVal", noSideEffect.}
|
||||
|
||||
proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal".}
|
||||
proc `floatVal=`*(n: PNimrodNode, val: BiggestFloat) {.magic: "NSetFloatVal".}
|
||||
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol".}
|
||||
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
|
||||
proc `intVal=`*(n: PNimrodNode, val: BiggestInt) {.magic: "NSetIntVal", noSideEffect.}
|
||||
proc `floatVal=`*(n: PNimrodNode, val: BiggestFloat) {.magic: "NSetFloatVal", noSideEffect.}
|
||||
proc `symbol=`*(n: PNimrodNode, val: PNimrodSymbol) {.magic: "NSetSymbol", noSideEffect.}
|
||||
proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent", noSideEffect.}
|
||||
#proc `typ=`*(n: PNimrodNode, typ: typedesc) {.magic: "NSetType".}
|
||||
# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you
|
||||
# can easily do:
|
||||
@@ -166,24 +169,24 @@ proc `ident=`*(n: PNimrodNode, val: TNimrodIdent) {.magic: "NSetIdent".}
|
||||
# let fake = semCheck(2.0)
|
||||
# bracket[0] = fake # constructs a mixed array with ints and floats!
|
||||
|
||||
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal".}
|
||||
proc `strVal=`*(n: PNimrodNode, val: string) {.magic: "NSetStrVal", noSideEffect.}
|
||||
|
||||
proc newNimNode*(kind: TNimrodNodeKind,
|
||||
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode".}
|
||||
n: PNimrodNode=nil): PNimrodNode {.magic: "NNewNimNode", noSideEffect.}
|
||||
|
||||
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode".}
|
||||
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree".}
|
||||
proc copyNimNode*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimNode", noSideEffect.}
|
||||
proc copyNimTree*(n: PNimrodNode): PNimrodNode {.magic: "NCopyNimTree", noSideEffect.}
|
||||
|
||||
proc error*(msg: string) {.magic: "NError".}
|
||||
proc error*(msg: string) {.magic: "NError", gcsafe.}
|
||||
## writes an error message at compile time
|
||||
|
||||
proc warning*(msg: string) {.magic: "NWarning".}
|
||||
proc warning*(msg: string) {.magic: "NWarning", gcsafe.}
|
||||
## writes a warning message at compile time
|
||||
|
||||
proc hint*(msg: string) {.magic: "NHint".}
|
||||
proc hint*(msg: string) {.magic: "NHint", gcsafe.}
|
||||
## writes a hint message at compile time
|
||||
|
||||
proc newStrLitNode*(s: string): PNimrodNode {.compileTime.} =
|
||||
proc newStrLitNode*(s: string): PNimrodNode {.compileTime, noSideEffect.} =
|
||||
## creates a string literal node from `s`
|
||||
result = newNimNode(nnkStrLit)
|
||||
result.strVal = s
|
||||
@@ -219,7 +222,7 @@ type
|
||||
## any other means in the language currently)
|
||||
|
||||
proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
|
||||
magic: "NBindSym".}
|
||||
magic: "NBindSym", noSideEffect.}
|
||||
## creates a node that binds `ident` to a symbol node. The bound symbol
|
||||
## may be an overloaded symbol.
|
||||
## If ``rule == brClosed`` either an ``nkClosedSymChoice`` tree is
|
||||
@@ -230,11 +233,11 @@ proc bindSym*(ident: string, rule: TBindSymRule = brClosed): PNimrodNode {.
|
||||
## returned even if the symbol is not ambiguous.
|
||||
|
||||
proc genSym*(kind: TNimrodSymKind = nskLet; ident = ""): PNimrodNode {.
|
||||
magic: "NGenSym".}
|
||||
magic: "NGenSym", noSideEffect.}
|
||||
## generates a fresh symbol that is guaranteed to be unique. The symbol
|
||||
## needs to occur in a declaration context.
|
||||
|
||||
proc callsite*(): PNimrodNode {.magic: "NCallSite".}
|
||||
proc callsite*(): PNimrodNode {.magic: "NCallSite", gcsafe.}
|
||||
## returns the AST if the invokation expression that invoked this macro.
|
||||
|
||||
proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||
@@ -242,19 +245,19 @@ proc toStrLit*(n: PNimrodNode): PNimrodNode {.compileTime.} =
|
||||
## in a string literal node
|
||||
return newStrLitNode(repr(n))
|
||||
|
||||
proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo".}
|
||||
proc lineinfo*(n: PNimrodNode): string {.magic: "NLineInfo", noSideEffect.}
|
||||
## returns the position the node appears in the original source file
|
||||
## in the form filename(line, col)
|
||||
|
||||
proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst".}
|
||||
proc parseExpr*(s: string): PNimrodNode {.magic: "ParseExprToAst", noSideEffect.}
|
||||
## Compiles the passed string to its AST representation.
|
||||
## Expects a single expression.
|
||||
|
||||
proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst".}
|
||||
proc parseStmt*(s: string): PNimrodNode {.magic: "ParseStmtToAst", noSideEffect.}
|
||||
## Compiles the passed string to its AST representation.
|
||||
## Expects one or more statements.
|
||||
|
||||
proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
|
||||
proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst", noSideEffect.}
|
||||
## Obtains the AST nodes returned from a macro or template invocation.
|
||||
## Example:
|
||||
##
|
||||
@@ -263,7 +266,7 @@ proc getAst*(macroOrTemplate: expr): PNimrodNode {.magic: "ExpandToAst".}
|
||||
## macro FooMacro() =
|
||||
## var ast = getAst(BarTemplate())
|
||||
|
||||
proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst".}
|
||||
proc quote*(bl: stmt, op = "``"): PNimrodNode {.magic: "QuoteAst", noSideEffect.}
|
||||
## Quasi-quoting operator.
|
||||
## Accepts an expression or a block and returns the AST that represents it.
|
||||
## Within the quoted AST, you are able to interpolate PNimrodNode expressions
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import os, oids, tables, strutils, macros
|
||||
|
||||
import rawsockets
|
||||
@@ -31,7 +33,7 @@ export TPort
|
||||
|
||||
type
|
||||
PFutureBase* = ref object of PObject
|
||||
cb: proc () {.closure.}
|
||||
cb: proc () {.closure,gcsafe.}
|
||||
finished: bool
|
||||
|
||||
PFuture*[T] = ref object of PFutureBase
|
||||
@@ -68,7 +70,7 @@ proc fail*[T](future: PFuture[T], error: ref EBase) =
|
||||
if future.cb != nil:
|
||||
future.cb()
|
||||
|
||||
proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
|
||||
proc `callback=`*(future: PFutureBase, cb: proc () {.closure,gcsafe.}) =
|
||||
## Sets the callback proc to be called when the future completes.
|
||||
##
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
@@ -80,7 +82,7 @@ proc `callback=`*(future: PFutureBase, cb: proc () {.closure.}) =
|
||||
future.cb()
|
||||
|
||||
proc `callback=`*[T](future: PFuture[T],
|
||||
cb: proc (future: PFuture[T]) {.closure.}) =
|
||||
cb: proc (future: PFuture[T]) {.closure,gcsafe.}) =
|
||||
## Sets the callback proc to be called when the future completes.
|
||||
##
|
||||
## If future has already completed then ``cb`` will be called immediately.
|
||||
@@ -122,7 +124,7 @@ when defined(windows) or defined(nimdoc):
|
||||
TCompletionData* = object
|
||||
sock: TAsyncFD
|
||||
cb: proc (sock: TAsyncFD, bytesTransferred: DWORD,
|
||||
errcode: TOSErrorCode) {.closure.}
|
||||
errcode: TOSErrorCode) {.closure,gcsafe.}
|
||||
|
||||
PDispatcher* = ref object
|
||||
ioPort: THandle
|
||||
@@ -237,7 +239,7 @@ when defined(windows) or defined(nimdoc):
|
||||
let func =
|
||||
cast[proc (s: TSocketHandle, name: ptr TSockAddr, namelen: cint,
|
||||
lpSendBuffer: pointer, dwSendDataLength: dword,
|
||||
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall.}](connectExPtr)
|
||||
lpdwBytesSent: PDWORD, lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](connectExPtr)
|
||||
|
||||
result = func(s, name, namelen, lpSendBuffer, dwSendDataLength, lpdwBytesSent,
|
||||
lpOverlapped)
|
||||
@@ -251,7 +253,7 @@ when defined(windows) or defined(nimdoc):
|
||||
cast[proc (listenSock, acceptSock: TSocketHandle, lpOutputBuffer: pointer,
|
||||
dwReceiveDataLength, dwLocalAddressLength,
|
||||
dwRemoteAddressLength: DWORD, lpdwBytesReceived: PDWORD,
|
||||
lpOverlapped: POverlapped): bool {.stdcall.}](acceptExPtr)
|
||||
lpOverlapped: POverlapped): bool {.stdcall,gcsafe.}](acceptExPtr)
|
||||
result = func(listenSock, acceptSock, lpOutputBuffer, dwReceiveDataLength,
|
||||
dwLocalAddressLength, dwRemoteAddressLength, lpdwBytesReceived,
|
||||
lpOverlapped)
|
||||
@@ -268,7 +270,7 @@ when defined(windows) or defined(nimdoc):
|
||||
dwReceiveDataLength, dwLocalAddressLength,
|
||||
dwRemoteAddressLength: DWORD, LocalSockaddr: ptr ptr TSockAddr,
|
||||
LocalSockaddrLength: lpint, RemoteSockaddr: ptr ptr TSockAddr,
|
||||
RemoteSockaddrLength: lpint) {.stdcall.}](getAcceptExSockAddrsPtr)
|
||||
RemoteSockaddrLength: lpint) {.stdcall,gcsafe.}](getAcceptExSockAddrsPtr)
|
||||
|
||||
func(lpOutputBuffer, dwReceiveDataLength, dwLocalAddressLength,
|
||||
dwRemoteAddressLength, LocalSockaddr, LocalSockaddrLength,
|
||||
@@ -537,7 +539,7 @@ else:
|
||||
from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK
|
||||
type
|
||||
TAsyncFD* = distinct cint
|
||||
TCallback = proc (sock: TAsyncFD): bool {.closure.}
|
||||
TCallback = proc (sock: TAsyncFD): bool {.closure,gcsafe.}
|
||||
|
||||
PData* = ref object of PObject
|
||||
sock: TAsyncFD
|
||||
@@ -757,7 +759,7 @@ proc accept*(socket: TAsyncFD): PFuture[TAsyncFD] =
|
||||
|
||||
template createCb*(retFutureSym, iteratorNameSym: expr): stmt {.immediate.} =
|
||||
var nameIterVar = iteratorNameSym
|
||||
proc cb {.closure.} =
|
||||
proc cb {.closure,gcsafe.} =
|
||||
if not nameIterVar.finished:
|
||||
var next = nameIterVar()
|
||||
if next == nil:
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import sockets, os
|
||||
|
||||
## This module implements an asynchronous event loop together with asynchronous sockets
|
||||
@@ -98,13 +100,13 @@ type
|
||||
fd*: TSocketHandle
|
||||
deleVal*: PObject
|
||||
|
||||
handleRead*: proc (h: PObject) {.nimcall.}
|
||||
handleWrite*: proc (h: PObject) {.nimcall.}
|
||||
handleError*: proc (h: PObject) {.nimcall.}
|
||||
hasDataBuffered*: proc (h: PObject): bool {.nimcall.}
|
||||
handleRead*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
handleWrite*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
handleError*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
hasDataBuffered*: proc (h: PObject): bool {.nimcall, gcsafe.}
|
||||
|
||||
open*: bool
|
||||
task*: proc (h: PObject) {.nimcall.}
|
||||
task*: proc (h: PObject) {.nimcall, gcsafe.}
|
||||
mode*: TFileMode
|
||||
|
||||
PDelegate* = ref TDelegate
|
||||
@@ -118,13 +120,13 @@ type
|
||||
socket: TSocket
|
||||
info: TInfo
|
||||
|
||||
handleRead*: proc (s: PAsyncSocket) {.closure.}
|
||||
handleWrite: proc (s: PAsyncSocket) {.closure.}
|
||||
handleConnect*: proc (s: PAsyncSocket) {.closure.}
|
||||
handleRead*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
handleConnect*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
|
||||
handleAccept*: proc (s: PAsyncSocket) {.closure.}
|
||||
handleAccept*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
|
||||
handleTask*: proc (s: PAsyncSocket) {.closure.}
|
||||
handleTask*: proc (s: PAsyncSocket) {.closure, gcsafe.}
|
||||
|
||||
lineBuffer: TaintedString ## Temporary storage for ``readLine``
|
||||
sendBuffer: string ## Temporary storage for ``send``
|
||||
@@ -213,7 +215,7 @@ proc asyncSockHandleRead(h: PObject) =
|
||||
else:
|
||||
PAsyncSocket(h).handleAccept(PAsyncSocket(h))
|
||||
|
||||
proc close*(sock: PAsyncSocket)
|
||||
proc close*(sock: PAsyncSocket) {.gcsafe.}
|
||||
proc asyncSockHandleWrite(h: PObject) =
|
||||
when defined(ssl):
|
||||
if PAsyncSocket(h).socket.isSSL and not
|
||||
@@ -254,7 +256,7 @@ proc asyncSockHandleWrite(h: PObject) =
|
||||
PAsyncSocket(h).deleg.mode = fmRead
|
||||
|
||||
when defined(ssl):
|
||||
proc asyncSockDoHandshake(h: PObject) =
|
||||
proc asyncSockDoHandshake(h: PObject) {.gcsafe.} =
|
||||
if PAsyncSocket(h).socket.isSSL and not
|
||||
PAsyncSocket(h).socket.gotHandshake:
|
||||
if PAsyncSocket(h).sslNeedAccept:
|
||||
@@ -437,7 +439,7 @@ proc isSendDataBuffered*(s: PAsyncSocket): bool =
|
||||
return s.sendBuffer.len != 0
|
||||
|
||||
proc setHandleWrite*(s: PAsyncSocket,
|
||||
handleWrite: proc (s: PAsyncSocket) {.closure.}) =
|
||||
handleWrite: proc (s: PAsyncSocket) {.closure, gcsafe.}) =
|
||||
## Setter for the ``handleWrite`` event.
|
||||
##
|
||||
## To remove this event you should use the ``delHandleWrite`` function.
|
||||
|
||||
@@ -377,7 +377,7 @@ proc setCookie*(name, value: string) =
|
||||
write(stdout, "Set-Cookie: ", name, "=", value, "\n")
|
||||
|
||||
var
|
||||
gcookies: PStringTable = nil
|
||||
gcookies {.threadvar.}: PStringTable
|
||||
|
||||
proc getCookie*(name: string): TaintedString =
|
||||
## Gets a cookie. If no cookie of `name` exists, "" is returned.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import sockets, strutils, parseutils, times, os, asyncio
|
||||
|
||||
## This module **partially** implements an FTP client as specified
|
||||
@@ -41,7 +43,7 @@ type
|
||||
dummyA, dummyB: pointer # workaround a Nimrod API issue
|
||||
asyncCSock: PAsyncSocket
|
||||
asyncDSock: PAsyncSocket
|
||||
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.}
|
||||
handleEvent*: proc (ftp: PAsyncFTPClient, ev: TFTPEvent){.closure,gcsafe.}
|
||||
disp: PDispatcher
|
||||
asyncDSockID: PDelegate
|
||||
user, pass: string
|
||||
@@ -59,7 +61,7 @@ type
|
||||
JRetrText, JRetr, JStore
|
||||
|
||||
TFTPJob = object
|
||||
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall.}
|
||||
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall, gcsafe.}
|
||||
case typ*: FTPJobType
|
||||
of JRetrText:
|
||||
lines: string
|
||||
@@ -148,7 +150,8 @@ proc assertReply(received: TaintedString, expected: varargs[string]) =
|
||||
[expected.join("' or '"), received.string])
|
||||
|
||||
proc createJob(ftp: PFTPClient,
|
||||
prc: proc (ftp: PFTPClient, async: bool): bool {.nimcall.},
|
||||
prc: proc (ftp: PFTPClient, async: bool): bool {.
|
||||
nimcall,gcsafe.},
|
||||
cmd: FTPJobType) =
|
||||
if ftp.jobInProgress:
|
||||
raise newException(EFTP, "Unable to do two jobs at once.")
|
||||
@@ -558,7 +561,7 @@ proc csockHandleRead(s: PAsyncSocket, ftp: PAsyncFTPClient) =
|
||||
|
||||
proc asyncFTPClient*(address: string, port = TPort(21),
|
||||
user, pass = "",
|
||||
handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure.} =
|
||||
handleEvent: proc (ftp: PAsyncFTPClient, ev: TFTPEvent) {.closure,gcsafe.} =
|
||||
(proc (ftp: PAsyncFTPClient, ev: TFTPEvent) = discard)): PAsyncFTPClient =
|
||||
## Create a ``PAsyncFTPClient`` object.
|
||||
##
|
||||
|
||||
@@ -477,7 +477,7 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
||||
s.path = data.substr(i, last-1)
|
||||
|
||||
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSocket,
|
||||
path, query: string): bool {.closure.},
|
||||
path, query: string): bool {.closure, gcsafe.},
|
||||
port = TPort(80), address = "",
|
||||
reuseAddr = false): PAsyncHTTPServer =
|
||||
## Creates an Asynchronous HTTP server at ``port``.
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import sockets, strutils, parseutils, times, asyncio, os
|
||||
|
||||
type
|
||||
@@ -41,7 +43,7 @@ type
|
||||
nick, user, realname, serverPass: string
|
||||
case isAsync: bool
|
||||
of true:
|
||||
handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||
handleEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure, gcsafe.}
|
||||
asyncSock: PAsyncSocket
|
||||
myDispatcher: PDispatcher
|
||||
of false:
|
||||
@@ -445,7 +447,7 @@ proc asyncIRC*(address: string, port: TPort = 6667.TPort,
|
||||
realname = "NimrodBot", serverPass = "",
|
||||
joinChans: seq[string] = @[],
|
||||
msgLimit: bool = true,
|
||||
ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure.}
|
||||
ircEvent: proc (irc: PAsyncIRC, ev: TIRCEvent) {.closure,gcsafe.}
|
||||
): PAsyncIRC =
|
||||
## Use this function if you want to use asyncio's dispatcher.
|
||||
##
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
## Basic math routines for Nimrod.
|
||||
## This module is available for the JavaScript target.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
{.push debugger:off .} # the user does not want to trace a part
|
||||
# of the standard library!
|
||||
|
||||
@@ -127,25 +129,25 @@ proc variance*(x: openArray[float]): float {.noSideEffect.} =
|
||||
result = result + diff*diff
|
||||
result = result / toFloat(len(x))
|
||||
|
||||
proc random*(max: int): int
|
||||
proc random*(max: int): int {.gcsafe.}
|
||||
## returns a random number in the range 0..max-1. The sequence of
|
||||
## random number is always the same, unless `randomize` is called
|
||||
## which initializes the random number generator with a "random"
|
||||
## number, i.e. a tickcount.
|
||||
|
||||
when not defined(windows):
|
||||
proc random*(max: float): float
|
||||
proc random*(max: float): float {.gcsafe.}
|
||||
## returns a random number in the range 0..<max. The sequence of
|
||||
## random number is always the same, unless `randomize` is called
|
||||
## which initializes the random number generator with a "random"
|
||||
## number, i.e. a tickcount. This is currently not supported for windows.
|
||||
|
||||
proc randomize*()
|
||||
proc randomize*() {.gcsafe.}
|
||||
## initializes the random number generator with a "random"
|
||||
## number, i.e. a tickcount. Note: Does nothing for the JavaScript target,
|
||||
## as JavaScript does not support this.
|
||||
|
||||
proc randomize*(seed: int)
|
||||
proc randomize*(seed: int) {.gcsafe.}
|
||||
## initializes the random number generator with a specific seed.
|
||||
## Note: Does nothing for the JavaScript target,
|
||||
## as JavaScript does not support this.
|
||||
@@ -227,8 +229,8 @@ else:
|
||||
result = int(floor(mathrandom() * float(max)))
|
||||
proc random(max: float): float =
|
||||
result = float(mathrandom() * float(max))
|
||||
proc randomize() = nil
|
||||
proc randomize(seed: int) = nil
|
||||
proc randomize() = discard
|
||||
proc randomize(seed: int) = discard
|
||||
|
||||
proc sqrt*(x: float): float {.importc: "Math.sqrt", nodecl.}
|
||||
proc ln*(x: float): float {.importc: "Math.log", nodecl.}
|
||||
|
||||
@@ -1564,7 +1564,7 @@ when defined(windows):
|
||||
# ourselves. This has the additional benefit that the program's behaviour
|
||||
# is always the same -- independent of the used C compiler.
|
||||
var
|
||||
ownArgv: seq[string]
|
||||
ownArgv {.threadvar.}: seq[string]
|
||||
|
||||
proc paramCount*(): int {.rtl, extern: "nos$1", tags: [FReadIO].} =
|
||||
## Returns the number of `command line arguments`:idx: given to the
|
||||
|
||||
@@ -70,7 +70,7 @@ const
|
||||
SymChars: TCharSet = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.',
|
||||
'/', '\\'}
|
||||
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken)
|
||||
proc rawGetTok(c: var TCfgParser, tok: var TToken) {.gcsafe.}
|
||||
|
||||
proc open*(c: var TCfgParser, input: PStream, filename: string,
|
||||
lineOffset = 0) {.
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
## **Warning:** The API of this module is unstable, and therefore is subject
|
||||
## to change.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
import sockets, strutils, os, strtabs, asyncio
|
||||
|
||||
type
|
||||
@@ -82,7 +84,7 @@ type
|
||||
|
||||
TAsyncScgiState = object
|
||||
handleRequest: proc (client: PAsyncSocket,
|
||||
input: string, headers: PStringTable) {.closure.}
|
||||
input: string, headers: PStringTable) {.closure,gcsafe.}
|
||||
asyncServer: PAsyncSocket
|
||||
disp: PDispatcher
|
||||
PAsyncScgiState* = ref TAsyncScgiState
|
||||
@@ -150,7 +152,7 @@ proc writeStatusOkTextContent*(c: TSocket, contentType = "text/html") =
|
||||
"Content-Type: $1\r\L\r\L" % contentType)
|
||||
|
||||
proc run*(handleRequest: proc (client: TSocket, input: string,
|
||||
headers: PStringTable): bool {.nimcall.},
|
||||
headers: PStringTable): bool {.nimcall,gcsafe.},
|
||||
port = TPort(4000)) =
|
||||
## encapsulates the SCGI object and main loop.
|
||||
var s: TScgiState
|
||||
@@ -246,7 +248,8 @@ proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
|
||||
s.disp.register(client)
|
||||
|
||||
proc open*(handleRequest: proc (client: PAsyncSocket,
|
||||
input: string, headers: PStringTable) {.closure.},
|
||||
input: string, headers: PStringTable) {.
|
||||
closure, gcsafe.},
|
||||
port = TPort(4000), address = "127.0.0.1",
|
||||
reuseAddr = false): PAsyncScgiState =
|
||||
## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
## Asynchronous sockets are supported, however a better alternative is to use
|
||||
## the `asyncio <asyncio.html>`_ module.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
{.deadCodeElim: on.}
|
||||
|
||||
when hostOS == "solaris":
|
||||
@@ -544,7 +546,7 @@ proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) {.
|
||||
else:
|
||||
SSLError("Unknown error")
|
||||
|
||||
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].}
|
||||
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [], gcsafe.}
|
||||
## Sets blocking mode on socket
|
||||
|
||||
when defined(ssl):
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
## interface for Nimrod file objects (`TFile`) and strings. Other modules
|
||||
## may provide other implementations for this standard stream interface.
|
||||
|
||||
include "system/inclrtl"
|
||||
|
||||
proc newEIO(msg: string): ref EIO =
|
||||
new(result)
|
||||
result.msg = msg
|
||||
@@ -23,15 +25,15 @@ type
|
||||
## here shouldn't be used directly. They are
|
||||
## accessible so that a stream implementation
|
||||
## can override them.
|
||||
closeImpl*: proc (s: PStream) {.nimcall, tags: [].}
|
||||
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [].}
|
||||
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [].}
|
||||
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [].}
|
||||
closeImpl*: proc (s: PStream) {.nimcall, tags: [], gcsafe.}
|
||||
atEndImpl*: proc (s: PStream): bool {.nimcall, tags: [], gcsafe.}
|
||||
setPositionImpl*: proc (s: PStream, pos: int) {.nimcall, tags: [], gcsafe.}
|
||||
getPositionImpl*: proc (s: PStream): int {.nimcall, tags: [], gcsafe.}
|
||||
readDataImpl*: proc (s: PStream, buffer: pointer,
|
||||
bufLen: int): int {.nimcall, tags: [FReadIO].}
|
||||
bufLen: int): int {.nimcall, tags: [FReadIO], gcsafe.}
|
||||
writeDataImpl*: proc (s: PStream, buffer: pointer, bufLen: int) {.nimcall,
|
||||
tags: [FWriteIO].}
|
||||
flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO].}
|
||||
tags: [FWriteIO], gcsafe.}
|
||||
flushImpl*: proc (s: PStream) {.nimcall, tags: [FWriteIO], gcsafe.}
|
||||
|
||||
proc flush*(s: PStream) =
|
||||
## flushes the buffers that the stream `s` might use.
|
||||
|
||||
@@ -135,38 +135,38 @@ type
|
||||
months*: int ## The number of months
|
||||
years*: int ## The number of years
|
||||
|
||||
proc getTime*(): TTime {.tags: [FTime].}
|
||||
proc getTime*(): TTime {.tags: [FTime], gcsafe.}
|
||||
## gets the current calendar time as a UNIX epoch value (number of seconds
|
||||
## elapsed since 1970) with integer precission. Use epochTime for higher
|
||||
## resolution.
|
||||
proc getLocalTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [].}
|
||||
proc getLocalTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [], gcsafe.}
|
||||
## converts the calendar time `t` to broken-time representation,
|
||||
## expressed relative to the user's specified time zone.
|
||||
proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [].}
|
||||
proc getGMTime*(t: TTime): TTimeInfo {.tags: [FTime], raises: [], gcsafe.}
|
||||
## converts the calendar time `t` to broken-down time representation,
|
||||
## expressed in Coordinated Universal Time (UTC).
|
||||
|
||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [].}
|
||||
proc timeInfoToTime*(timeInfo: TTimeInfo): TTime {.tags: [], gcsafe.}
|
||||
## converts a broken-down time structure to
|
||||
## calendar time representation. The function ignores the specified
|
||||
## contents of the structure members `weekday` and `yearday` and recomputes
|
||||
## them from the other information in the broken-down time structure.
|
||||
|
||||
proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [].}
|
||||
proc fromSeconds*(since1970: float): TTime {.tags: [], raises: [], gcsafe.}
|
||||
## Takes a float which contains the number of seconds since the unix epoch and
|
||||
## returns a time object.
|
||||
|
||||
proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [].} =
|
||||
proc fromSeconds*(since1970: int64): TTime {.tags: [], raises: [], gcsafe.} =
|
||||
## Takes an int which contains the number of seconds since the unix epoch and
|
||||
## returns a time object.
|
||||
fromSeconds(float(since1970))
|
||||
|
||||
proc toSeconds*(time: TTime): float {.tags: [], raises: [].}
|
||||
proc toSeconds*(time: TTime): float {.tags: [], raises: [], gcsafe.}
|
||||
## Returns the time in seconds since the unix epoch.
|
||||
|
||||
proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [].}
|
||||
proc `$` *(timeInfo: TTimeInfo): string {.tags: [], raises: [], gcsafe.}
|
||||
## converts a `TTimeInfo` object to a string representation.
|
||||
proc `$` *(time: TTime): string {.tags: [], raises: [].}
|
||||
proc `$` *(time: TTime): string {.tags: [], raises: [], gcsafe.}
|
||||
## converts a calendar time to a string representation.
|
||||
|
||||
proc `-`*(a, b: TTime): int64 {.
|
||||
@@ -189,14 +189,15 @@ proc `==`*(a, b: TTime): bool {.
|
||||
result = a - b == 0
|
||||
|
||||
when not defined(JS):
|
||||
proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime], raises: [].}
|
||||
proc getTzname*(): tuple[nonDST, DST: string] {.tags: [FTime], raises: [],
|
||||
gcsafe.}
|
||||
## returns the local timezone; ``nonDST`` is the name of the local non-DST
|
||||
## timezone, ``DST`` is the name of the local DST timezone.
|
||||
|
||||
proc getTimezone*(): int {.tags: [FTime], raises: [].}
|
||||
proc getTimezone*(): int {.tags: [FTime], raises: [], gcsafe.}
|
||||
## returns the offset of the local (non-DST) timezone in seconds west of UTC.
|
||||
|
||||
proc getStartMilsecs*(): int {.deprecated, tags: [FTime].}
|
||||
proc getStartMilsecs*(): int {.deprecated, tags: [FTime], gcsafe.}
|
||||
## get the miliseconds from the start of the program. **Deprecated since
|
||||
## version 0.8.10.** Use ``epochTime`` or ``cpuTime`` instead.
|
||||
|
||||
|
||||
@@ -192,9 +192,6 @@ when defined(nimNewShared):
|
||||
type
|
||||
`shared`* {.magic: "Shared".}
|
||||
guarded* {.magic: "Guarded".}
|
||||
else:
|
||||
{.pragma: gcsafe.}
|
||||
#{.pragma: gcsafe.}
|
||||
|
||||
const NoFakeVars* = defined(NimrodVM) ## true if the backend doesn't support \
|
||||
## "fake variables" like 'var EBADF {.importc.}: cint'.
|
||||
|
||||
@@ -29,7 +29,7 @@ type
|
||||
region: TMemRegion
|
||||
PRawChannel = ptr TRawChannel
|
||||
TLoadStoreMode = enum mStore, mLoad
|
||||
TChannel*[TMsg] = TRawChannel ## a channel for thread communication
|
||||
TChannel* {.gcsafe.}[TMsg] = TRawChannel ## a channel for thread communication
|
||||
|
||||
const ChannelDeadMask = -2
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
# -> defined(useNimRtl) or appType == "lib" and not defined(createNimRtl)
|
||||
# 3) Exported into nimrtl.
|
||||
# -> appType == "lib" and defined(createNimRtl)
|
||||
when not defined(nimNewShared):
|
||||
{.pragma: gcsafe.}
|
||||
|
||||
when defined(createNimRtl):
|
||||
when defined(useNimRtl):
|
||||
@@ -24,7 +26,7 @@ when defined(createNimRtl):
|
||||
{.error: "nimrtl must be built as a library!".}
|
||||
|
||||
when defined(createNimRtl):
|
||||
{.pragma: rtl, exportc: "nimrtl_$1", dynlib.}
|
||||
{.pragma: rtl, exportc: "nimrtl_$1", dynlib, gcsafe.}
|
||||
{.pragma: inl.}
|
||||
{.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.}
|
||||
elif defined(useNimRtl):
|
||||
@@ -34,11 +36,11 @@ elif defined(useNimRtl):
|
||||
const nimrtl* = "nimrtl.dylib"
|
||||
else:
|
||||
const nimrtl* = "libnimrtl.so"
|
||||
{.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl.}
|
||||
{.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.}
|
||||
{.pragma: inl.}
|
||||
{.pragma: compilerRtl, compilerproc, importc: "nimrtl_$1", dynlib: nimrtl.}
|
||||
else:
|
||||
{.pragma: rtl.}
|
||||
{.pragma: rtl, gcsafe.}
|
||||
{.pragma: inl, inline.}
|
||||
{.pragma: compilerRtl, compilerproc.}
|
||||
|
||||
|
||||
@@ -256,9 +256,9 @@ type
|
||||
## that **must not** be part of a message! Use
|
||||
## a ``TThreadId`` for that.
|
||||
when TArg is void:
|
||||
dataFn: proc () {.nimcall.}
|
||||
dataFn: proc () {.nimcall, gcsafe.}
|
||||
else:
|
||||
dataFn: proc (m: TArg) {.nimcall.}
|
||||
dataFn: proc (m: TArg) {.nimcall, gcsafe.}
|
||||
data: TArg
|
||||
TThreadId*[TArg] = ptr TThread[TArg] ## the current implementation uses
|
||||
## a pointer as a thread ID.
|
||||
|
||||
Reference in New Issue
Block a user