Merge branch 'master' of github.com:Araq/Nimrod
This commit is contained in:
commit
993cdb82b6
25 changed files with 3237 additions and 2222 deletions
|
|
@ -89,13 +89,13 @@ import sockets, os
|
|||
## getSocket(s).accept(client)
|
||||
|
||||
when defined(windows):
|
||||
from winlean import TTimeVal, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
from winlean import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
else:
|
||||
from posix import TTimeVal, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
from posix import TTimeVal, TSocketHandle, TFdSet, FD_ZERO, FD_SET, FD_ISSET, select
|
||||
|
||||
type
|
||||
TDelegate* = object
|
||||
fd*: cint
|
||||
fd*: TSocketHandle
|
||||
deleVal*: PObject
|
||||
|
||||
handleRead*: proc (h: PObject) {.nimcall.}
|
||||
|
|
@ -213,6 +213,7 @@ proc asyncSockHandleRead(h: PObject) =
|
|||
else:
|
||||
PAsyncSocket(h).handleAccept(PAsyncSocket(h))
|
||||
|
||||
proc close*(sock: PAsyncSocket)
|
||||
proc asyncSockHandleWrite(h: PObject) =
|
||||
when defined(ssl):
|
||||
if PAsyncSocket(h).socket.isSSL and not
|
||||
|
|
@ -230,15 +231,19 @@ proc asyncSockHandleWrite(h: PObject) =
|
|||
else:
|
||||
if PAsyncSocket(h).sendBuffer != "":
|
||||
let sock = PAsyncSocket(h)
|
||||
let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
|
||||
assert bytesSent > 0
|
||||
if bytesSent != sock.sendBuffer.len:
|
||||
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
|
||||
elif bytesSent == sock.sendBuffer.len:
|
||||
sock.sendBuffer = ""
|
||||
|
||||
if PAsyncSocket(h).handleWrite != nil:
|
||||
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
|
||||
try:
|
||||
let bytesSent = sock.socket.sendAsync(sock.sendBuffer)
|
||||
assert bytesSent > 0
|
||||
if bytesSent != sock.sendBuffer.len:
|
||||
sock.sendBuffer = sock.sendBuffer[bytesSent .. -1]
|
||||
elif bytesSent == sock.sendBuffer.len:
|
||||
sock.sendBuffer = ""
|
||||
|
||||
if PAsyncSocket(h).handleWrite != nil:
|
||||
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
|
||||
except EOS:
|
||||
# Most likely the socket closed before the full buffer could be sent to it.
|
||||
sock.close() # TODO: Provide a handleError for users?
|
||||
else:
|
||||
if PAsyncSocket(h).handleWrite != nil:
|
||||
PAsyncSocket(h).handleWrite(PAsyncSocket(h))
|
||||
|
|
|
|||
|
|
@ -1,117 +1,127 @@
|
|||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements a base64 encoder and decoder.
|
||||
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
var numLines = (total + lineLen - 1) div lineLen
|
||||
#
|
||||
#
|
||||
# Nimrod's Runtime Library
|
||||
# (c) Copyright 2010 Andreas Rumpf
|
||||
#
|
||||
# See the file "copying.txt", included in this
|
||||
# distribution, for details about the copyright.
|
||||
#
|
||||
|
||||
## This module implements a base64 encoder and decoder.
|
||||
|
||||
const
|
||||
cb64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
template encodeInternal(s: expr, lineLen: int, newLine: string): stmt {.immediate.} =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
var total = ((len(s) + 2) div 3) * 4
|
||||
var numLines = (total + lineLen - 1) div lineLen
|
||||
if numLines > 0: inc(total, (numLines-1) * newLine.len)
|
||||
|
||||
result = newString(total)
|
||||
var i = 0
|
||||
var r = 0
|
||||
var currLine = 0
|
||||
while i < s.len - 2:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
var c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
if currLine >= lineLen and i != s.len-2:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
|
||||
if i < s.len-1:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
elif i < s.len:
|
||||
var a = ord(s[i])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[(a and 3) shl 4]
|
||||
result[r+2] = '='
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
assert(r == result.len)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
of 'A'..'Z': result = ord(b) - ord('A')
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## decodes a string in base64 representation back into its original form.
|
||||
## Whitespace is skipped.
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||
result = newString(total)
|
||||
|
||||
var i = 0
|
||||
var r = 0
|
||||
while true:
|
||||
while s[i] in Whitespace: inc(i)
|
||||
if i < s.len-3:
|
||||
var a = s[i].decodeByte
|
||||
var b = s[i+1].decodeByte
|
||||
var c = s[i+2].decodeByte
|
||||
var d = s[i+3].decodeByte
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
inc(r, 3)
|
||||
inc(i, 4)
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText]
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
|
||||
|
||||
result = newString(total)
|
||||
var i = 0
|
||||
var r = 0
|
||||
var currLine = 0
|
||||
while i < s.len - 2:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
var c = ord(s[i+2])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2) or ((c and 0xC0) shr 6)]
|
||||
result[r+3] = cb64[c and 0x3F]
|
||||
inc(r, 4)
|
||||
inc(i, 3)
|
||||
inc(currLine, 4)
|
||||
if currLine >= lineLen and i != s.len-2:
|
||||
for x in items(newLine):
|
||||
result[r] = x
|
||||
inc(r)
|
||||
currLine = 0
|
||||
|
||||
if i < s.len-1:
|
||||
var a = ord(s[i])
|
||||
var b = ord(s[i+1])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[((a and 3) shl 4) or ((b and 0xF0) shr 4)]
|
||||
result[r+2] = cb64[((b and 0x0F) shl 2)]
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
elif i < s.len:
|
||||
var a = ord(s[i])
|
||||
result[r] = cb64[a shr 2]
|
||||
result[r+1] = cb64[(a and 3) shl 4]
|
||||
result[r+2] = '='
|
||||
result[r+3] = '='
|
||||
if r+4 != result.len:
|
||||
setLen(result, r+4)
|
||||
else:
|
||||
assert(r == result.len)
|
||||
|
||||
proc encode*[T:TInteger|char](s: openarray[T], lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
|
||||
proc encode*(s: string, lineLen = 75, newLine="\13\10"): string =
|
||||
## encodes `s` into base64 representation. After `lineLen` characters, a
|
||||
## `newline` is added.
|
||||
encodeInternal(s, lineLen, newLine)
|
||||
|
||||
proc decodeByte(b: char): int {.inline.} =
|
||||
case b
|
||||
of '+': result = ord('>')
|
||||
of '0'..'9': result = ord(b) + 4
|
||||
of 'A'..'Z': result = ord(b) - ord('A')
|
||||
of 'a'..'z': result = ord(b) - 71
|
||||
else: result = 63
|
||||
|
||||
proc decode*(s: string): string =
|
||||
## decodes a string in base64 representation back into its original form.
|
||||
## Whitespace is skipped.
|
||||
const Whitespace = {' ', '\t', '\v', '\r', '\l', '\f'}
|
||||
var total = ((len(s) + 3) div 4) * 3
|
||||
# total is an upper bound, as we will skip arbitrary whitespace:
|
||||
result = newString(total)
|
||||
|
||||
var i = 0
|
||||
var r = 0
|
||||
while true:
|
||||
while s[i] in Whitespace: inc(i)
|
||||
if i < s.len-3:
|
||||
var a = s[i].decodeByte
|
||||
var b = s[i+1].decodeByte
|
||||
var c = s[i+2].decodeByte
|
||||
var d = s[i+3].decodeByte
|
||||
|
||||
result[r] = chr((a shl 2) and 0xff or ((b shr 4) and 0x03))
|
||||
result[r+1] = chr((b shl 4) and 0xff or ((c shr 2) and 0x0F))
|
||||
result[r+2] = chr((c shl 6) and 0xff or (d and 0x3F))
|
||||
inc(r, 3)
|
||||
inc(i, 4)
|
||||
else: break
|
||||
assert i == s.len
|
||||
# adjust the length:
|
||||
if i > 0 and s[i-1] == '=':
|
||||
dec(r)
|
||||
if i > 1 and s[i-2] == '=': dec(r)
|
||||
setLen(result, r)
|
||||
|
||||
when isMainModule:
|
||||
assert encode("leasure.") == "bGVhc3VyZS4="
|
||||
assert encode("easure.") == "ZWFzdXJlLg=="
|
||||
assert encode("asure.") == "YXN1cmUu"
|
||||
assert encode("sure.") == "c3VyZS4="
|
||||
|
||||
const longText = """Man is distinguished, not only by his reason, but by this
|
||||
singular passion from other animals, which is a lust of the mind,
|
||||
that by a perseverance of delight in the continued and indefatigable
|
||||
generation of knowledge, exceeds the short vehemence of any carnal
|
||||
pleasure."""
|
||||
const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.",
|
||||
"asure.", longText]
|
||||
for t in items(tests):
|
||||
assert decode(encode(t)) == t
|
||||
|
||||
|
|
|
|||
581
lib/pure/collections/LockFreeHash.nim
Normal file
581
lib/pure/collections/LockFreeHash.nim
Normal file
|
|
@ -0,0 +1,581 @@
|
|||
#nimrod c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim
|
||||
|
||||
import baseutils, unsigned, math, hashes
|
||||
|
||||
|
||||
|
||||
const
|
||||
minTableSize = 8
|
||||
reProbeLimit = 12
|
||||
minCopyWork = 4096
|
||||
intSize = sizeof(int)
|
||||
|
||||
|
||||
|
||||
when sizeof(int) == 4: # 32bit
|
||||
type
|
||||
TRaw = range[0..1073741823]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 32 bit platform
|
||||
|
||||
elif sizeof(int) == 8: # 64bit
|
||||
type
|
||||
TRaw = range[0..4611686018427387903]
|
||||
## The range of uint values that can be stored directly in a value slot
|
||||
## when on a 64 bit platform
|
||||
else: echo("unsupported platform")
|
||||
|
||||
type
|
||||
TEntry = tuple
|
||||
key: int
|
||||
value: int
|
||||
|
||||
TEntryArr = ptr array[0..10_000_000, TEntry]
|
||||
|
||||
PConcTable[K,V] = ptr object {.pure.}
|
||||
len: int
|
||||
used: int
|
||||
active: int
|
||||
copyIdx: int
|
||||
copyDone: int
|
||||
next: PConcTable[K,V]
|
||||
data: TEntryArr
|
||||
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Create a new table
|
||||
proc newLFTable*[K,V](size: int = minTableSize): PConcTable[K,V] =
|
||||
let
|
||||
dataLen = max(nextPowerOfTwo(size), minTableSize)
|
||||
dataSize = dataLen*sizeof(TEntry)
|
||||
dataMem = allocShared0(dataSize)
|
||||
tableSize = 7 * intSize
|
||||
tableMem = allocShared0(tableSize)
|
||||
table = cast[PConcTable[K,V]](tableMem)
|
||||
table.len = dataLen
|
||||
table.used = 0
|
||||
table.active = 0
|
||||
table.copyIdx = 0
|
||||
table.copyDone = 0
|
||||
table.next = nil
|
||||
table.data = cast[TEntryArr](dataMem)
|
||||
result = table
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# Delete a table
|
||||
proc deleteConcTable[K,V](tbl: PConcTable[K,V]) =
|
||||
deallocShared(tbl.data)
|
||||
deallocShared(tbl)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc `[]`[K,V](table: var PConcTable[K,V], i: int): var TEntry {.inline.} =
|
||||
table.data[i]
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# State flags stored in ptr
|
||||
|
||||
|
||||
proc pack[T](x: T): int {.inline.} =
|
||||
result = (cast[int](x) shl 2)
|
||||
#echo("packKey ",cast[int](x) , " -> ", result)
|
||||
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
proc pop(x: int): int {.inline.} =
|
||||
result = x and 0xFFFFFFFC'i32
|
||||
|
||||
# Pop the raw value off of our Key or Val
|
||||
proc popRaw(x: int): int {.inline.} =
|
||||
result = x shr 2
|
||||
|
||||
# Pop the flags off returning a 4 byte aligned ptr to our Key or Val
|
||||
proc popPtr[V](x: int): ptr V {.inline.} =
|
||||
result = cast[ptr V](pop(x))
|
||||
#echo("popPtr " & $x & " -> " & $cast[int](result))
|
||||
|
||||
# Ghost (sentinel)
|
||||
# K or V is no longer valid use new table
|
||||
const Ghost = 0xFFFFFFFC
|
||||
proc isGhost(x: int): bool {.inline.} =
|
||||
result = x == 0xFFFFFFFC
|
||||
|
||||
# Tombstone
|
||||
# applied to V = K is dead
|
||||
proc isTomb(x: int): bool {.inline.} =
|
||||
result = (x and 0x00000002) != 0
|
||||
|
||||
proc setTomb(x: int): int {.inline.} =
|
||||
result = x or 0x00000002
|
||||
|
||||
# Prime
|
||||
# K or V is in new table copied from old
|
||||
proc isPrime(x: int): bool {.inline.} =
|
||||
result = (x and 0x00000001) != 0
|
||||
|
||||
proc setPrime(x: int): int {.inline.} =
|
||||
result = x or 0x00000001
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
##This is for i32 only need to override for i64
|
||||
proc hashInt(x: int):int {.inline.} =
|
||||
var h = uint32(x) #shr 2'u32
|
||||
h = h xor (h shr 16'u32)
|
||||
h *= 0x85ebca6b'u32
|
||||
h = h xor (h shr 13'u32)
|
||||
h *= 0xc2b2ae35'u32
|
||||
h = h xor (h shr 16'u32)
|
||||
result = int(h)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc resize[K,V](self: PConcTable[K,V]): PConcTable[K,V] =
|
||||
var next = atomic_load_n(self.next.addr, ATOMIC_RELAXED)
|
||||
#echo("next = " & $cast[int](next))
|
||||
if next != nil:
|
||||
#echo("A new table already exists, copy in progress")
|
||||
return next
|
||||
var
|
||||
oldLen = atomic_load_n(self.len.addr, ATOMIC_RELAXED)
|
||||
newTable = newLFTable[K,V](oldLen*2)
|
||||
success = atomic_compare_exchange_n(self.next.addr, next.addr, newTable,
|
||||
false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if not success:
|
||||
echo("someone beat us to it! delete table we just created and return his " & $cast[int](next))
|
||||
deleteConcTable(newTable)
|
||||
return next
|
||||
else:
|
||||
echo("Created New Table! " & $cast[int](newTable) & " Size = " & $newTable.len)
|
||||
return newTable
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
#proc keyEQ[K](key1: ptr K, key2: ptr K): bool {.inline.} =
|
||||
proc keyEQ[K](key1: int, key2: int): bool {.inline.} =
|
||||
result = false
|
||||
when K is TRaw:
|
||||
if key1 == key2:
|
||||
result = true
|
||||
else:
|
||||
var
|
||||
p1 = popPtr[K](key1)
|
||||
p2 = popPtr[K](key2)
|
||||
if p1 != nil and p2 != nil:
|
||||
if cast[int](p1) == cast[int](p2):
|
||||
return true
|
||||
if p1[] == p2[]:
|
||||
return true
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc tableFull(self: var PConcTable[K,V]) : bool {.inline.} =
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc copySlot[K,V](idx: int, oldTbl: var PConcTable[K,V], newTbl: var PConcTable[K,V]): bool =
|
||||
#echo("Copy idx " & $idx)
|
||||
var
|
||||
oldVal = 0
|
||||
oldkey = 0
|
||||
ok = false
|
||||
result = false
|
||||
#Block the key so no other threads waste time here
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].key.addr, oldKey.addr,
|
||||
setTomb(oldKey), false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("oldKey was = " & $oldKey & " set it to tomb " & $setTomb(oldKey))
|
||||
#Prevent new values from appearing in the old table by priming
|
||||
oldVal = atomic_load_n(oldTbl[idx].value.addr, ATOMIC_RELAXED)
|
||||
while not isPrime(oldVal):
|
||||
var box = if oldVal == NULL or isTomb(oldVal) : oldVal.setTomb.setPrime
|
||||
else: oldVal.setPrime
|
||||
if atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
box, false, ATOMIC_RELAXED, ATOMIC_RELAXED):
|
||||
if isPrime(box) and isTomb(box):
|
||||
return true
|
||||
oldVal = box
|
||||
break
|
||||
#echo("oldVal was = ", oldVal, " set it to prime ", box)
|
||||
if isPrime(oldVal) and isTomb(oldVal):
|
||||
#when not (K is TRaw):
|
||||
# deallocShared(popPtr[K](oldKey))
|
||||
return false
|
||||
if isTomb(oldVal):
|
||||
echo("oldVal is Tomb!!!, should not happen")
|
||||
if pop(oldVal) != NULL:
|
||||
result = setVal(newTbl, pop(oldKey), pop(oldVal), NULL, true) == NULL
|
||||
if result:
|
||||
#echo("Copied a Slot! idx= " & $idx & " key= " & $oldKey & " val= " & $oldVal)
|
||||
else:
|
||||
#echo("copy slot failed")
|
||||
# Our copy is done so we disable the old slot
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(oldTbl[idx].value.addr, oldVal.addr,
|
||||
oldVal.setTomb.setPrime , false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("disabled old slot")
|
||||
#echo"---------------------"
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc promote[K,V](table: var PConcTable[K,V]) =
|
||||
var
|
||||
newData = atomic_load_n(table.next.data.addr, ATOMIC_RELAXED)
|
||||
newLen = atomic_load_n(table.next.len.addr, ATOMIC_RELAXED)
|
||||
newUsed = atomic_load_n(table.next.used.addr, ATOMIC_RELAXED)
|
||||
|
||||
deallocShared(table.data)
|
||||
atomic_store_n(table.data.addr, newData, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.len.addr, newLen, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.used.addr, newUsed, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.copyIdx.addr, 0, ATOMIC_RELAXED)
|
||||
atomic_store_n(table.copyDone.addr, 0, ATOMIC_RELAXED)
|
||||
deallocShared(table.next)
|
||||
atomic_store_n(table.next.addr, nil, ATOMIC_RELAXED)
|
||||
echo("new table swapped!")
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc checkAndPromote[K,V](table: var PConcTable[K,V], workDone: int): bool =
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
|
||||
ok: bool
|
||||
result = false
|
||||
if workDone > 0:
|
||||
#echo("len to copy =" & $oldLen)
|
||||
#echo("copyDone + workDone = " & $copyDone & " + " & $workDone)
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(table.copyDone.addr, copyDone.addr,
|
||||
copyDone + workDone, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#if ok: echo("set copyDone")
|
||||
# If the copy is done we can promote this table
|
||||
if copyDone + workDone >= oldLen:
|
||||
# Swap new data
|
||||
#echo("work is done!")
|
||||
table.promote
|
||||
result = true
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc copySlotAndCheck[K,V](table: var PConcTable[K,V], idx: int):
|
||||
PConcTable[K,V] =
|
||||
var
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil and copySlot(idx, table, newTable):
|
||||
#echo("copied a single slot, idx = " & $idx)
|
||||
if checkAndPromote(table, 1): return table
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc helpCopy[K,V](table: var PConcTable[K,V]): PConcTable[K,V] =
|
||||
var
|
||||
newTable = cast[PConcTable[K,V]](atomic_load_n(table.next.addr, ATOMIC_RELAXED))
|
||||
result = newTable
|
||||
if newTable != nil:
|
||||
var
|
||||
oldLen = atomic_load_n(table.len.addr, ATOMIC_RELAXED)
|
||||
copyDone = atomic_load_n(table.copyDone.addr, ATOMIC_RELAXED)
|
||||
copyIdx = 0
|
||||
work = min(oldLen, minCopyWork)
|
||||
#panicStart = -1
|
||||
workDone = 0
|
||||
if copyDone < oldLen:
|
||||
var ok: bool
|
||||
while not ok:
|
||||
ok = atomic_compare_exchange_n(table.copyIdx.addr, copyIdx.addr,
|
||||
copyIdx + work, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
#echo("copy idx = ", copyIdx)
|
||||
for i in 0..work-1:
|
||||
var idx = (copyIdx + i) and (oldLen - 1)
|
||||
if copySlot(idx, table, newTable):
|
||||
workDone += 1
|
||||
if workDone > 0:
|
||||
#echo("did work ", workDone, " on thread ", cast[int](myThreadID[pointer]()))
|
||||
if checkAndPromote(table, workDone): return table
|
||||
# In case a thread finished all the work then got stalled before promotion
|
||||
if checkAndPromote(table, 0): return table
|
||||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc setVal[K,V](table: var PConcTable[K,V], key: int, val: int,
|
||||
expVal: int, match: bool): int =
|
||||
#echo("-try set- in table ", " key = ", (popPtr[K](key)[]), " val = ", val)
|
||||
when K is TRaw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
var
|
||||
nextTable: PConcTable[K,V]
|
||||
probes = 1
|
||||
# spin until we find a key slot or build and jump to next table
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
#echo("try set idx = " & $idx & "for" & $key)
|
||||
var
|
||||
probedKey = NULL
|
||||
openKey = atomic_compare_exchange_n(table[idx].key.addr, probedKey.addr,
|
||||
key, false, ATOMIC_RELAXED, ATOMIC_RELAXED)
|
||||
if openKey:
|
||||
if val.isTomb:
|
||||
#echo("val was tomb, bail, no reason to set an open slot to tomb")
|
||||
return val
|
||||
#increment used slots
|
||||
#echo("found an open slot, total used = " &
|
||||
#$atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED))
|
||||
discard atomic_add_fetch(table.used.addr, 1, ATOMIC_RELAXED)
|
||||
break # We found an open slot
|
||||
#echo("set idx ", idx, " key = ", key, " probed = ", probedKey)
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("we found the matching slot")
|
||||
break # We found a matching slot
|
||||
if (not(expVal != NULL and match)) and (probes >= reProbeLimit or key.isTomb):
|
||||
if key.isTomb: echo("Key is Tombstone")
|
||||
#if probes >= reProbeLimit: echo("Too much probing " & $probes)
|
||||
#echo("try to resize")
|
||||
#create next bigger table
|
||||
nextTable = resize(table)
|
||||
#help do some copying
|
||||
#echo("help copy old table to new")
|
||||
nextTable = helpCopy(table)
|
||||
#now setVal in the new table instead
|
||||
#echo("jumping to next table to set val")
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
else:
|
||||
idx += 1
|
||||
probes += 1
|
||||
# Done spinning for a new slot
|
||||
var oldVal = atomic_load_n(table[idx].value.addr, ATOMIC_RELAXED)
|
||||
if val == oldVal:
|
||||
#echo("this val is alredy in the slot")
|
||||
return oldVal
|
||||
nextTable = atomic_load_n(table.next.addr, ATOMIC_SEQ_CST)
|
||||
if nextTable == nil and
|
||||
((oldVal == NULL and
|
||||
(probes >= reProbeLimit or table.used / table.len > 0.8)) or
|
||||
(isPrime(oldVal))):
|
||||
if table.used / table.len > 0.8: echo("resize because usage ratio = " &
|
||||
$(table.used / table.len))
|
||||
if isPrime(oldVal): echo("old val isPrime, should be a rare mem ordering event")
|
||||
nextTable = resize(table)
|
||||
if nextTable != nil:
|
||||
#echo("tomb old slot then set in new table")
|
||||
nextTable = copySlotAndCheck(table,idx)
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
# Finaly ready to add new val to table
|
||||
while true:
|
||||
if match and oldVal != expVal:
|
||||
#echo("set failed, no match oldVal= " & $oldVal & " expVal= " & $expVal)
|
||||
return oldVal
|
||||
if atomic_compare_exchange_n(table[idx].value.addr, oldVal.addr,
|
||||
val, false, ATOMIC_RELEASE, ATOMIC_RELAXED):
|
||||
#echo("val set at table " & $cast[int](table))
|
||||
if expVal != NULL:
|
||||
if (oldVal == NULL or isTomb(oldVal)) and not isTomb(val):
|
||||
discard atomic_add_fetch(table.active.addr, 1, ATOMIC_RELAXED)
|
||||
elif not (oldVal == NULL or isTomb(oldVal)) and isTomb(val):
|
||||
discard atomic_add_fetch(table.active.addr, -1, ATOMIC_RELAXED)
|
||||
if oldVal == NULL and expVal != NULL:
|
||||
return setTomb(oldVal)
|
||||
else: return oldVal
|
||||
if isPrime(oldVal):
|
||||
nextTable = copySlotAndCheck(table, idx)
|
||||
return setVal(nextTable, key, val, expVal, match)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
proc getVal[K,V](table: var PConcTable[K,V], key: int): int =
|
||||
#echo("-try get- key = " & $key)
|
||||
when K is TRaw:
|
||||
var idx = hashInt(key)
|
||||
else:
|
||||
var idx = popPtr[K](key)[].hash
|
||||
#echo("get idx ", idx)
|
||||
var
|
||||
probes = 0
|
||||
val: int
|
||||
while true:
|
||||
idx = idx and (table.len - 1)
|
||||
var
|
||||
newTable: PConcTable[K,V] # = atomic_load_n(table.next.addr, ATOMIC_ACQUIRE)
|
||||
probedKey = atomic_load_n(table[idx].key.addr, ATOMIC_SEQ_CST)
|
||||
if keyEQ[K](probedKey, key):
|
||||
#echo("found key after ", probes+1)
|
||||
val = atomic_load_n(table[idx].value.addr, ATOMIC_ACQUIRE)
|
||||
if not isPrime(val):
|
||||
if isTomb(val):
|
||||
#echo("val was tomb but not prime")
|
||||
return NULL
|
||||
else:
|
||||
#echo("-GotIt- idx = ", idx, " key = ", key, " val ", val )
|
||||
return val
|
||||
else:
|
||||
newTable = copySlotAndCheck(table, idx)
|
||||
return getVal(newTable, key)
|
||||
else:
|
||||
#echo("probe ", probes, " idx = ", idx, " key = ", key, " found ", probedKey )
|
||||
if probes >= reProbeLimit*4 or key.isTomb:
|
||||
if newTable == nil:
|
||||
#echo("too many probes and no new table ", key, " ", idx )
|
||||
return NULL
|
||||
else:
|
||||
newTable = helpCopy(table)
|
||||
return getVal(newTable, key)
|
||||
idx += 1
|
||||
probes += 1
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
#proc set*(table: var PConcTable[TRaw,TRaw], key: TRaw, val: TRaw) =
|
||||
# discard setVal(table, pack(key), pack(key), NULL, false)
|
||||
|
||||
#proc set*[V](table: var PConcTable[TRaw,V], key: TRaw, val: ptr V) =
|
||||
# discard setVal(table, pack(key), cast[int](val), NULL, false)
|
||||
|
||||
proc set*[K,V](table: var PConcTable[K,V], key: var K, val: var V) =
|
||||
when not (K is TRaw):
|
||||
var newKey = cast[int](copyShared(key))
|
||||
else:
|
||||
var newKey = pack(key)
|
||||
when not (V is TRaw):
|
||||
var newVal = cast[int](copyShared(val))
|
||||
else:
|
||||
var newVal = pack(val)
|
||||
var oldPtr = pop(setVal(table, newKey, newVal, NULL, false))
|
||||
#echo("oldPtr = ", cast[int](oldPtr), " newPtr = ", cast[int](newPtr))
|
||||
when not (V is TRaw):
|
||||
if newVal != oldPtr and oldPtr != NULL:
|
||||
deallocShared(cast[ptr V](oldPtr))
|
||||
|
||||
|
||||
|
||||
proc get*[K,V](table: var PConcTable[K,V], key: var K): V =
|
||||
when not (V is TRaw):
|
||||
when not (K is TRaw):
|
||||
return popPtr[V](getVal(table, cast[int](key.addr)))[]
|
||||
else:
|
||||
return popPtr[V](getVal(table, pack(key)))[]
|
||||
else:
|
||||
when not (K is TRaw):
|
||||
return popRaw(getVal(table, cast[int](key.addr)))
|
||||
else:
|
||||
return popRaw(getVal(table, pack(key)))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#proc `[]`[K,V](table: var PConcTable[K,V], key: K): PEntry[K,V] {.inline.} =
|
||||
# getVal(table, key)
|
||||
|
||||
#proc `[]=`[K,V](table: var PConcTable[K,V], key: K, val: V): PEntry[K,V] {.inline.} =
|
||||
# setVal(table, key, val)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#Tests ----------------------------
|
||||
when isMainModule:
|
||||
import locks, times, mersenne
|
||||
|
||||
const
|
||||
numTests = 100000
|
||||
numThreads = 10
|
||||
|
||||
|
||||
|
||||
type
|
||||
TTestObj = tuple
|
||||
thr: int
|
||||
f0: int
|
||||
f1: int
|
||||
|
||||
TData = tuple[k: string,v: TTestObj]
|
||||
PDataArr = array[0..numTests-1, TData]
|
||||
Dict = PConcTable[string,TTestObj]
|
||||
|
||||
var
|
||||
thr: array[0..numThreads-1, TThread[Dict]]
|
||||
|
||||
table = newLFTable[string,TTestObj](8)
|
||||
rand = newMersenneTwister(2525)
|
||||
|
||||
proc createSampleData(len: int): PDataArr =
|
||||
#result = cast[PDataArr](allocShared0(sizeof(TData)*numTests))
|
||||
for i in 0..len-1:
|
||||
result[i].k = "mark" & $(i+1)
|
||||
#echo("mark" & $(i+1), " ", hash("mark" & $(i+1)))
|
||||
result[i].v.thr = 0
|
||||
result[i].v.f0 = i+1
|
||||
result[i].v.f1 = 0
|
||||
#echo("key = " & $(i+1) & " Val ptr = " & $cast[int](result[i].v.addr))
|
||||
|
||||
|
||||
|
||||
proc threadProc(tp: Dict) {.thread.} =
|
||||
var t = cpuTime();
|
||||
for i in 1..numTests:
|
||||
var key = "mark" & $(i)
|
||||
var got = table.get(key)
|
||||
got.thr = cast[int](myThreadID[pointer]())
|
||||
got.f1 = got.f1 + 1
|
||||
table.set(key, got)
|
||||
t = cpuTime() - t
|
||||
echo t
|
||||
|
||||
|
||||
var testData = createSampleData(numTests)
|
||||
|
||||
for i in 0..numTests-1:
|
||||
table.set(testData[i].k, testData[i].v)
|
||||
|
||||
var i = 0
|
||||
while i < numThreads:
|
||||
createThread(thr[i], threadProc, table)
|
||||
i += 1
|
||||
|
||||
joinThreads(thr)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
var fails = 0
|
||||
|
||||
for i in 0..numTests-1:
|
||||
var got = table.get(testData[i].k)
|
||||
if got.f0 != i+1 or got.f1 != numThreads:
|
||||
fails += 1
|
||||
echo(got)
|
||||
|
||||
echo("Failed read or write = ", fails)
|
||||
|
||||
|
||||
#for i in 1..numTests:
|
||||
# echo(i, " = ", hashInt(i) and 8191)
|
||||
|
||||
deleteConcTable(table)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
41
lib/pure/collections/baseutils.nim
Normal file
41
lib/pure/collections/baseutils.nim
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Useful Constants
|
||||
const NULL* = 0
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Memory Utility Functions
|
||||
|
||||
proc newHeap*[T](): ptr T =
|
||||
result = cast[ptr T](alloc0(sizeof(T)))
|
||||
|
||||
proc copyNew*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = alloc(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
proc copyTo*[T](val: var T, dest: int) =
|
||||
copyMem(pointer(dest), val.addr, sizeof(T))
|
||||
|
||||
proc allocType*[T](): pointer = alloc(sizeof(T))
|
||||
|
||||
proc newShared*[T](): ptr T =
|
||||
result = cast[ptr T](allocShared0(sizeof(T)))
|
||||
|
||||
proc copyShared*[T](x: var T): ptr T =
|
||||
var
|
||||
size = sizeof(T)
|
||||
mem = allocShared(size)
|
||||
copyMem(mem, x.addr, size)
|
||||
return cast[ptr T](mem)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
## Pointer arithmetic
|
||||
|
||||
proc `+`*(p: pointer, i: int): pointer {.inline.} =
|
||||
cast[pointer](cast[int](p) + i)
|
||||
|
|
@ -117,6 +117,57 @@ proc filter*[T](seq1: seq[T], pred: proc(item: T): bool {.closure.}): seq[T] =
|
|||
## assert f2 == @["yellow"]
|
||||
accumulateResult(filter(seq1, pred))
|
||||
|
||||
proc delete*[T](s: var seq[T], first=0, last=0) =
|
||||
## Deletes in `s` the items at position `first` .. `last`. This modifies
|
||||
## `s` itself, it does not return a copy.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
##.. code-block:: nimrod
|
||||
## let outcome = @[1,1,1,1,1,1,1,1]
|
||||
## var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||
## dest.delete(3, 8)
|
||||
## assert outcome == dest
|
||||
|
||||
var i = first
|
||||
var j = last+1
|
||||
var newLen = len(s)-j+i
|
||||
while i < newLen:
|
||||
s[i].shallowCopy(s[j])
|
||||
inc(i)
|
||||
inc(j)
|
||||
setlen(s, newLen)
|
||||
|
||||
proc insert*[T](dest: var seq[T], src: openArray[T], pos=0) =
|
||||
## Inserts items from `src` into `dest` at position `pos`. This modifies
|
||||
## `dest` itself, it does not return a copy.
|
||||
##
|
||||
## Example:
|
||||
##
|
||||
##.. code-block:: nimrod
|
||||
## var dest = @[1,1,1,1,1,1,1,1]
|
||||
## let
|
||||
## src = @[2,2,2,2,2,2]
|
||||
## outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||
## dest.insert(src, 3)
|
||||
## assert dest == outcome
|
||||
|
||||
var j = len(dest) - 1
|
||||
var i = len(dest) + len(src) - 1
|
||||
dest.setLen(i + 1)
|
||||
|
||||
# Move items after `pos` to the end of the sequence.
|
||||
while j >= pos:
|
||||
dest[i].shallowCopy(dest[j])
|
||||
dec(i)
|
||||
dec(j)
|
||||
# Insert items from `dest` into `dest` at `pos`
|
||||
inc(j)
|
||||
for item in src:
|
||||
dest[j] = item
|
||||
inc(j)
|
||||
|
||||
|
||||
template filterIt*(seq1, pred: expr): expr {.immediate.} =
|
||||
## Returns a new sequence with all the items that fulfilled the predicate.
|
||||
##
|
||||
|
|
@ -312,4 +363,22 @@ when isMainModule:
|
|||
assert multiplication == 495, "Multiplication is (5*(9*(11)))"
|
||||
assert concatenation == "nimrodiscool"
|
||||
|
||||
block: # delete tests
|
||||
let outcome = @[1,1,1,1,1,1,1,1]
|
||||
var dest = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||
dest.delete(3, 8)
|
||||
assert outcome == dest, """\
|
||||
Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||
is [1,1,1,1,1,1,1,1]"""
|
||||
|
||||
block: # insert tests
|
||||
var dest = @[1,1,1,1,1,1,1,1]
|
||||
let
|
||||
src = @[2,2,2,2,2,2]
|
||||
outcome = @[1,1,1,2,2,2,2,2,2,1,1,1,1,1]
|
||||
dest.insert(src, 3)
|
||||
assert dest == outcome, """\
|
||||
Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1]
|
||||
at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]"""
|
||||
|
||||
echo "Finished doc tests"
|
||||
|
|
|
|||
|
|
@ -224,11 +224,13 @@ type
|
|||
TAsyncHTTPServer = object of TServer
|
||||
asyncSocket: PAsyncSocket
|
||||
|
||||
proc open*(s: var TServer, port = TPort(80)) =
|
||||
proc open*(s: var TServer, port = TPort(80), reuseAddr = false) =
|
||||
## creates a new server at port `port`. If ``port == 0`` a free port is
|
||||
## acquired that can be accessed later by the ``port`` proc.
|
||||
s.socket = socket(AF_INET)
|
||||
if s.socket == InvalidSocket: OSError(OSLastError())
|
||||
if reuseAddr:
|
||||
s.socket.setSockOpt(OptReuseAddr, True)
|
||||
bindAddr(s.socket, port)
|
||||
listen(s.socket)
|
||||
|
||||
|
|
@ -475,7 +477,8 @@ proc nextAsync(s: PAsyncHTTPServer) =
|
|||
|
||||
proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSocket,
|
||||
path, query: string): bool {.closure.},
|
||||
port = TPort(80), address = ""): PAsyncHTTPServer =
|
||||
port = TPort(80), address = "",
|
||||
reuseAddr = false): PAsyncHTTPServer =
|
||||
## Creates an Asynchronous HTTP server at ``port``.
|
||||
var capturedRet: PAsyncHTTPServer
|
||||
new(capturedRet)
|
||||
|
|
@ -486,6 +489,8 @@ proc asyncHTTPServer*(handleRequest: proc (server: PAsyncHTTPServer, client: TSo
|
|||
let quit = handleRequest(capturedRet, capturedRet.client, capturedRet.path,
|
||||
capturedRet.query)
|
||||
if quit: capturedRet.asyncSocket.close()
|
||||
if reuseAddr:
|
||||
capturedRet.asyncSocket.setSockOpt(OptReuseAddr, True)
|
||||
|
||||
capturedRet.asyncSocket.bindAddr(port, address)
|
||||
capturedRet.asyncSocket.listen()
|
||||
|
|
|
|||
39
lib/pure/mersenne.nim
Normal file
39
lib/pure/mersenne.nim
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import unsigned
|
||||
|
||||
type
|
||||
TMersenneTwister* = object
|
||||
mt: array[0..623, uint32]
|
||||
index: int
|
||||
|
||||
proc newMersenneTwister*(seed: int): TMersenneTwister =
|
||||
result.index = 0
|
||||
result.mt[0]= uint32(seed)
|
||||
for i in 1..623'u32:
|
||||
result.mt[i]= (0x6c078965'u32 * (result.mt[i-1] xor (result.mt[i-1] shr 30'u32)) + i)
|
||||
|
||||
proc generateNumbers(m: var TMersenneTwister) =
|
||||
for i in 0..623:
|
||||
var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32)
|
||||
m.mt[i] = m.mt[(i+397) mod 624] xor uint32(y shr 1'u32)
|
||||
if (y mod 2'u32) != 0:
|
||||
m.mt[i] = m.mt[i] xor 0x9908b0df'u32
|
||||
|
||||
proc getNum*(m: var TMersenneTwister): int =
|
||||
if m.index == 0:
|
||||
generateNumbers(m)
|
||||
var y = m.mt[m.index]
|
||||
y = y xor (y shr 11'u32)
|
||||
y = y xor ((7'u32 shl y) and 0x9d2c5680'u32)
|
||||
y = y xor ((15'u32 shl y) and 0xefc60000'u32)
|
||||
y = y xor (y shr 18'u32)
|
||||
m.index = (m.index+1) mod 624
|
||||
return int(y)
|
||||
|
||||
|
||||
|
||||
# Test
|
||||
when isMainModule:
|
||||
var mt = newMersenneTwister(2525)
|
||||
|
||||
for i in 0..99:
|
||||
echo mt.getNum
|
||||
|
|
@ -24,10 +24,10 @@ type
|
|||
TProcess = object of TObject
|
||||
when defined(windows):
|
||||
FProcessHandle: Thandle
|
||||
inputHandle, outputHandle, errorHandle: TFileHandle
|
||||
inHandle, outHandle, errHandle: TFileHandle
|
||||
id: THandle
|
||||
else:
|
||||
inputHandle, outputHandle, errorHandle: TFileHandle
|
||||
inHandle, outHandle, errHandle: TFileHandle
|
||||
inStream, outStream, errStream: PStream
|
||||
id: TPid
|
||||
exitCode: cint
|
||||
|
|
@ -113,23 +113,47 @@ proc peekExitCode*(p: PProcess): int {.tags: [].}
|
|||
## return -1 if the process is still running. Otherwise the process' exit code
|
||||
|
||||
proc inputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## returns ``p``'s input stream for writing to
|
||||
## returns ``p``'s input stream for writing to.
|
||||
##
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## is closed when closing the PProcess ``p``.
|
||||
|
||||
proc outputStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## returns ``p``'s output stream for reading from
|
||||
## returns ``p``'s output stream for reading from.
|
||||
##
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## is closed when closing the PProcess ``p``.
|
||||
|
||||
proc errorStream*(p: PProcess): PStream {.rtl, extern: "nosp$1", tags: [].}
|
||||
## returns ``p``'s output stream for reading from
|
||||
## returns ``p``'s error stream for reading from.
|
||||
##
|
||||
## **Warning**: The returned `PStream` should not be closed manually as it
|
||||
## is closed when closing the PProcess ``p``.
|
||||
|
||||
proc inputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1",
|
||||
tags: [].} =
|
||||
## returns ``p``'s input file handle for writing to.
|
||||
##
|
||||
## **Warning**: The returned `TFileHandle` should not be closed manually as
|
||||
## it is closed when closing the PProcess ``p``.
|
||||
result = p.inHandle
|
||||
|
||||
proc outputHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1",
|
||||
tags: [].} =
|
||||
## returns ``p``'s output file handle for reading from.
|
||||
##
|
||||
## **Warning**: The returned `TFileHandle` should not be closed manually as
|
||||
## it is closed when closing the PProcess ``p``.
|
||||
result = p.outHandle
|
||||
|
||||
proc errorHandle*(p: PProcess): TFileHandle {.rtl, extern: "nosp$1",
|
||||
tags: [].} =
|
||||
## returns ``p``'s error file handle for reading from.
|
||||
##
|
||||
## **Warning**: The returned `TFileHandle` should not be closed manually as
|
||||
## it is closed when closing the PProcess ``p``.
|
||||
result = p.errHandle
|
||||
|
||||
when defined(macosx) or defined(bsd):
|
||||
const
|
||||
CTL_HW = 6
|
||||
|
|
@ -212,8 +236,8 @@ proc execProcesses*(cmds: openArray[string],
|
|||
inc(i)
|
||||
if i > high(cmds): break
|
||||
for j in 0..m-1:
|
||||
if q[j] != nil: close(q[j])
|
||||
result = max(waitForExit(q[j]), result)
|
||||
if q[j] != nil: close(q[j])
|
||||
else:
|
||||
for i in 0..high(cmds):
|
||||
var p = startCmd(cmds[i], options=options)
|
||||
|
|
@ -339,16 +363,16 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
HE = HO
|
||||
else:
|
||||
CreatePipeHandles(HE, Si.hStdError)
|
||||
result.inputHandle = TFileHandle(hi)
|
||||
result.outputHandle = TFileHandle(ho)
|
||||
result.errorHandle = TFileHandle(he)
|
||||
result.inHandle = TFileHandle(hi)
|
||||
result.outHandle = TFileHandle(ho)
|
||||
result.errHandle = TFileHandle(he)
|
||||
else:
|
||||
SI.hStdError = GetStdHandle(STD_ERROR_HANDLE)
|
||||
SI.hStdInput = GetStdHandle(STD_INPUT_HANDLE)
|
||||
SI.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE)
|
||||
result.inputHandle = TFileHandle(si.hStdInput)
|
||||
result.outputHandle = TFileHandle(si.hStdOutput)
|
||||
result.errorHandle = TFileHandle(si.hStdError)
|
||||
result.inHandle = TFileHandle(si.hStdInput)
|
||||
result.outHandle = TFileHandle(si.hStdOutput)
|
||||
result.errHandle = TFileHandle(si.hStdError)
|
||||
|
||||
var cmdl: cstring
|
||||
when false: # poUseShell in options:
|
||||
|
|
@ -389,9 +413,9 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
proc close(p: PProcess) =
|
||||
when false:
|
||||
# somehow this does not work on Windows:
|
||||
discard CloseHandle(p.inputHandle)
|
||||
discard CloseHandle(p.outputHandle)
|
||||
discard CloseHandle(p.errorHandle)
|
||||
discard CloseHandle(p.inHandle)
|
||||
discard CloseHandle(p.outHandle)
|
||||
discard CloseHandle(p.errHandle)
|
||||
discard CloseHandle(p.FProcessHandle)
|
||||
|
||||
proc suspend(p: PProcess) =
|
||||
|
|
@ -425,13 +449,13 @@ when defined(Windows) and not defined(useNimRtl):
|
|||
return res
|
||||
|
||||
proc inputStream(p: PProcess): PStream =
|
||||
result = newFileHandleStream(p.inputHandle)
|
||||
result = newFileHandleStream(p.inHandle)
|
||||
|
||||
proc outputStream(p: PProcess): PStream =
|
||||
result = newFileHandleStream(p.outputHandle)
|
||||
result = newFileHandleStream(p.outHandle)
|
||||
|
||||
proc errorStream(p: PProcess): PStream =
|
||||
result = newFileHandleStream(p.errorHandle)
|
||||
result = newFileHandleStream(p.errHandle)
|
||||
|
||||
proc execCmd(command: string): int =
|
||||
var
|
||||
|
|
@ -626,20 +650,20 @@ elif not defined(useNimRtl):
|
|||
|
||||
if poParentStreams in options:
|
||||
# does not make much sense, but better than nothing:
|
||||
result.inputHandle = 0
|
||||
result.outputHandle = 1
|
||||
result.inHandle = 0
|
||||
result.outHandle = 1
|
||||
if poStdErrToStdOut in options:
|
||||
result.errorHandle = result.outputHandle
|
||||
result.errHandle = result.outHandle
|
||||
else:
|
||||
result.errorHandle = 2
|
||||
result.errHandle = 2
|
||||
else:
|
||||
result.inputHandle = p_stdin[writeIdx]
|
||||
result.outputHandle = p_stdout[readIdx]
|
||||
result.inHandle = p_stdin[writeIdx]
|
||||
result.outHandle = p_stdout[readIdx]
|
||||
if poStdErrToStdOut in options:
|
||||
result.errorHandle = result.outputHandle
|
||||
result.errHandle = result.outHandle
|
||||
discard close(p_stderr[readIdx])
|
||||
else:
|
||||
result.errorHandle = p_stderr[readIdx]
|
||||
result.errHandle = p_stderr[readIdx]
|
||||
discard close(p_stderr[writeIdx])
|
||||
discard close(p_stdin[readIdx])
|
||||
discard close(p_stdout[writeIdx])
|
||||
|
|
@ -648,9 +672,9 @@ elif not defined(useNimRtl):
|
|||
if p.inStream != nil: close(p.inStream)
|
||||
if p.outStream != nil: close(p.outStream)
|
||||
if p.errStream != nil: close(p.errStream)
|
||||
discard close(p.inputHandle)
|
||||
discard close(p.outputHandle)
|
||||
discard close(p.errorHandle)
|
||||
discard close(p.inHandle)
|
||||
discard close(p.outHandle)
|
||||
discard close(p.errHandle)
|
||||
|
||||
proc suspend(p: PProcess) =
|
||||
if kill(-p.id, SIGSTOP) != 0'i32: OSError(OSLastError())
|
||||
|
|
@ -696,17 +720,17 @@ elif not defined(useNimRtl):
|
|||
|
||||
proc inputStream(p: PProcess): PStream =
|
||||
if p.inStream == nil:
|
||||
createStream(p.inStream, p.inputHandle, fmWrite)
|
||||
createStream(p.inStream, p.inHandle, fmWrite)
|
||||
return p.inStream
|
||||
|
||||
proc outputStream(p: PProcess): PStream =
|
||||
if p.outStream == nil:
|
||||
createStream(p.outStream, p.outputHandle, fmRead)
|
||||
createStream(p.outStream, p.outHandle, fmRead)
|
||||
return p.outStream
|
||||
|
||||
proc errorStream(p: PProcess): PStream =
|
||||
if p.errStream == nil:
|
||||
createStream(p.errStream, p.errorHandle, fmRead)
|
||||
createStream(p.errStream, p.errHandle, fmRead)
|
||||
return p.errStream
|
||||
|
||||
proc csystem(cmd: cstring): cint {.nodecl, importc: "system".}
|
||||
|
|
@ -717,14 +741,14 @@ elif not defined(useNimRtl):
|
|||
proc createFdSet(fd: var TFdSet, s: seq[PProcess], m: var int) =
|
||||
FD_ZERO(fd)
|
||||
for i in items(s):
|
||||
m = max(m, int(i.outputHandle))
|
||||
FD_SET(cint(i.outputHandle), fd)
|
||||
m = max(m, int(i.outHandle))
|
||||
FD_SET(cint(i.outHandle), fd)
|
||||
|
||||
proc pruneProcessSet(s: var seq[PProcess], fd: var TFdSet) =
|
||||
var i = 0
|
||||
var L = s.len
|
||||
while i < L:
|
||||
if FD_ISSET(cint(s[i].outputHandle), fd) != 0'i32:
|
||||
if FD_ISSET(cint(s[i].outHandle), fd) != 0'i32:
|
||||
s[i] = s[L-1]
|
||||
dec(L)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -95,7 +95,8 @@ proc recvBuffer(s: var TScgiState, L: int) =
|
|||
scgiError("could not read all data")
|
||||
setLen(s.input, L)
|
||||
|
||||
proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1") =
|
||||
proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1",
|
||||
reuseAddr = False) =
|
||||
## opens a connection.
|
||||
s.bufLen = 4000
|
||||
s.input = newString(s.buflen) # will be reused
|
||||
|
|
@ -104,6 +105,8 @@ proc open*(s: var TScgiState, port = TPort(4000), address = "127.0.0.1") =
|
|||
new(s.client) # Initialise s.client for `next`
|
||||
if s.server == InvalidSocket: scgiError("could not open socket")
|
||||
#s.server.connect(connectionName, port)
|
||||
if reuseAddr:
|
||||
s.server.setSockOpt(OptReuseAddr, True)
|
||||
bindAddr(s.server, port, address)
|
||||
listen(s.server)
|
||||
|
||||
|
|
@ -243,7 +246,8 @@ proc handleAccept(sock: PAsyncSocket, s: PAsyncScgiState) =
|
|||
|
||||
proc open*(handleRequest: proc (client: PAsyncSocket,
|
||||
input: string, headers: PStringTable) {.closure.},
|
||||
port = TPort(4000), address = "127.0.0.1"): PAsyncScgiState =
|
||||
port = TPort(4000), address = "127.0.0.1",
|
||||
reuseAddr = false): PAsyncScgiState =
|
||||
## Creates an ``PAsyncScgiState`` object which serves as a SCGI server.
|
||||
##
|
||||
## After the execution of ``handleRequest`` the client socket will be closed
|
||||
|
|
@ -252,6 +256,8 @@ proc open*(handleRequest: proc (client: PAsyncSocket,
|
|||
new(cres)
|
||||
cres.asyncServer = AsyncSocket()
|
||||
cres.asyncServer.handleAccept = proc (s: PAsyncSocket) = handleAccept(s, cres)
|
||||
if reuseAddr:
|
||||
cres.asyncServer.setSockOpt(OptReuseAddr, True)
|
||||
bindAddr(cres.asyncServer, port, address)
|
||||
listen(cres.asyncServer)
|
||||
cres.handleRequest = handleRequest
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ when hostos == "solaris":
|
|||
|
||||
import os, parseutils
|
||||
from times import epochTime
|
||||
import unsigned
|
||||
|
||||
when defined(ssl):
|
||||
import openssl
|
||||
|
|
@ -62,7 +63,7 @@ const
|
|||
|
||||
type
|
||||
TSocketImpl = object ## socket type
|
||||
fd: cint
|
||||
fd: TSocketHandle
|
||||
case isBuffered: bool # determines whether this socket is buffered.
|
||||
of true:
|
||||
buffer: array[0..BufferSize, char]
|
||||
|
|
@ -82,7 +83,7 @@ type
|
|||
|
||||
TSocket* = ref TSocketImpl
|
||||
|
||||
TPort* = distinct int16 ## port type
|
||||
TPort* = distinct uint16 ## port type
|
||||
|
||||
TDomain* = enum ## domain, which specifies the protocol family of the
|
||||
## created socket. Other domains than those that are listed
|
||||
|
|
@ -118,6 +119,10 @@ type
|
|||
length*: int
|
||||
addrList*: seq[string]
|
||||
|
||||
TSOBool* = enum ## Boolean socket options.
|
||||
OptAcceptConn, OptBroadcast, OptDebug, OptDontRoute, OptKeepAlive,
|
||||
OptOOBInline, OptReuseAddr
|
||||
|
||||
TRecvLineResult* = enum ## result for recvLineAsync
|
||||
RecvFullLine, RecvPartialLine, RecvDisconnected, RecvFail
|
||||
|
||||
|
|
@ -126,7 +131,19 @@ type
|
|||
|
||||
ETimeout* = object of ESynch
|
||||
|
||||
proc newTSocket(fd: int32, isBuff: bool): TSocket =
|
||||
let
|
||||
InvalidSocket*: TSocket = nil ## invalid socket
|
||||
|
||||
when defined(windows):
|
||||
let
|
||||
OSInvalidSocket = winlean.INVALID_SOCKET
|
||||
else:
|
||||
let
|
||||
OSInvalidSocket = posix.INVALID_SOCKET
|
||||
|
||||
proc newTSocket(fd: TSocketHandle, isBuff: bool): TSocket =
|
||||
if fd == OSInvalidSocket:
|
||||
return nil
|
||||
new(result)
|
||||
result.fd = fd
|
||||
result.isBuffered = isBuff
|
||||
|
|
@ -134,9 +151,6 @@ proc newTSocket(fd: int32, isBuff: bool): TSocket =
|
|||
result.currPos = 0
|
||||
result.nonblocking = false
|
||||
|
||||
let
|
||||
InvalidSocket*: TSocket = nil ## invalid socket
|
||||
|
||||
proc `==`*(a, b: TPort): bool {.borrow.}
|
||||
## ``==`` for ports.
|
||||
|
||||
|
|
@ -211,7 +225,9 @@ else:
|
|||
|
||||
proc socket*(domain: TDomain = AF_INET, typ: TType = SOCK_STREAM,
|
||||
protocol: TProtocol = IPPROTO_TCP, buffered = true): TSocket =
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
## Creates a new socket; returns `InvalidSocket` if an error occurs.
|
||||
|
||||
# TODO: Perhaps this should just raise EOS when an error occurs.
|
||||
when defined(Windows):
|
||||
result = newTSocket(winlean.socket(ord(domain), ord(typ), ord(protocol)), buffered)
|
||||
else:
|
||||
|
|
@ -456,7 +472,7 @@ template acceptAddrPlain(noClientRet, successRet: expr,
|
|||
var sock = accept(server.fd, cast[ptr TSockAddr](addr(sockAddress)),
|
||||
addr(addrLen))
|
||||
|
||||
if sock < 0:
|
||||
if sock == OSInvalidSocket:
|
||||
let err = OSLastError()
|
||||
when defined(windows):
|
||||
if err.int32 == WSAEINPROGRESS:
|
||||
|
|
@ -529,7 +545,7 @@ proc acceptAddr*(server: TSocket, client: var TSocket, address: var string) {.
|
|||
SSLError("Unknown error")
|
||||
|
||||
proc setBlocking*(s: TSocket, blocking: bool) {.tags: [].}
|
||||
## sets blocking mode on socket
|
||||
## Sets blocking mode on socket
|
||||
|
||||
when defined(ssl):
|
||||
proc acceptAddrSSL*(server: TSocket, client: var TSocket,
|
||||
|
|
@ -623,24 +639,32 @@ proc close*(socket: TSocket) =
|
|||
discard SSLShutdown(socket.sslHandle)
|
||||
|
||||
proc getServByName*(name, proto: string): TServent {.tags: [FReadIO].} =
|
||||
## well-known getservbyname proc.
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the service name specified by ``name`` matches the s_name member
|
||||
## and the protocol name specified by ``proto`` matches the s_proto member.
|
||||
##
|
||||
## On posix this will search through the ``/etc/services`` file.
|
||||
when defined(Windows):
|
||||
var s = winlean.getservbyname(name, proto)
|
||||
else:
|
||||
var s = posix.getservbyname(name, proto)
|
||||
if s == nil: OSError(OSLastError())
|
||||
if s == nil: raise newException(EOS, "Service not found.")
|
||||
result.name = $s.s_name
|
||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||
result.port = TPort(s.s_port)
|
||||
result.proto = $s.s_proto
|
||||
|
||||
proc getServByPort*(port: TPort, proto: string): TServent {.tags: [FReadIO].} =
|
||||
## well-known getservbyport proc.
|
||||
## Searches the database from the beginning and finds the first entry for
|
||||
## which the port specified by ``port`` matches the s_port member and the
|
||||
## protocol name specified by ``proto`` matches the s_proto member.
|
||||
##
|
||||
## On posix this will search through the ``/etc/services`` file.
|
||||
when defined(Windows):
|
||||
var s = winlean.getservbyport(ze(int16(port)).cint, proto)
|
||||
else:
|
||||
var s = posix.getservbyport(ze(int16(port)).cint, proto)
|
||||
if s == nil: OSError(OSLastError())
|
||||
if s == nil: raise newException(EOS, "Service not found.")
|
||||
result.name = $s.s_name
|
||||
result.aliases = cstringArrayToSeq(s.s_aliases)
|
||||
result.port = TPort(s.s_port)
|
||||
|
|
@ -676,7 +700,7 @@ proc getHostByAddr*(ip: string): THostEnt {.tags: [FReadIO].} =
|
|||
result.length = int(s.h_length)
|
||||
|
||||
proc getHostByName*(name: string): THostEnt {.tags: [FReadIO].} =
|
||||
## well-known gethostbyname proc.
|
||||
## This function will lookup the IP address of a hostname.
|
||||
when defined(Windows):
|
||||
var s = winlean.gethostbyname(name)
|
||||
else:
|
||||
|
|
@ -714,6 +738,34 @@ proc setSockOptInt*(socket: TSocket, level, optname, optval: int) {.
|
|||
sizeof(value).TSockLen) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
|
||||
proc toCInt(opt: TSOBool): cint =
|
||||
case opt
|
||||
of OptAcceptConn: SO_ACCEPTCONN
|
||||
of OptBroadcast: SO_BROADCAST
|
||||
of OptDebug: SO_DEBUG
|
||||
of OptDontRoute: SO_DONTROUTE
|
||||
of OptKeepAlive: SO_KEEPALIVE
|
||||
of OptOOBInline: SO_OOBINLINE
|
||||
of OptReuseAddr: SO_REUSEADDR
|
||||
|
||||
proc getSockOpt*(socket: TSocket, opt: TSOBool, level = SOL_SOCKET): bool {.
|
||||
tags: [FReadIO].} =
|
||||
## Retrieves option ``opt`` as a boolean value.
|
||||
var res: cint
|
||||
var size = sizeof(res).TSockLen
|
||||
if getsockopt(socket.fd, cint(level), toCInt(opt),
|
||||
addr(res), addr(size)) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
result = res != 0
|
||||
|
||||
proc setSockOpt*(socket: TSocket, opt: TSOBool, value: bool, level = SOL_SOCKET) {.
|
||||
tags: [FWriteIO].} =
|
||||
## Sets option ``opt`` to a boolean value specified by ``value``.
|
||||
var valuei = cint(if value: 1 else: 0)
|
||||
if setsockopt(socket.fd, cint(level), toCInt(opt), addr(valuei),
|
||||
sizeof(valuei).TSockLen) < 0'i32:
|
||||
OSError(OSLastError())
|
||||
|
||||
proc connect*(socket: TSocket, address: string, port = TPort(0),
|
||||
af: TDomain = AF_INET) {.tags: [FReadIO].} =
|
||||
## Connects socket to ``address``:``port``. ``Address`` can be an IP address or a
|
||||
|
|
@ -866,11 +918,6 @@ proc timeValFromMilliseconds(timeout = 500): TTimeVal =
|
|||
var seconds = timeout div 1000
|
||||
result.tv_sec = seconds.int32
|
||||
result.tv_usec = ((timeout - seconds * 1000) * 1000).int32
|
||||
#proc recvfrom*(s: TWinSocket, buf: cstring, len, flags: cint,
|
||||
# fromm: ptr TSockAddr, fromlen: ptr cint): cint
|
||||
|
||||
#proc sendto*(s: TWinSocket, buf: cstring, len, flags: cint,
|
||||
# to: ptr TSockAddr, tolen: cint): cint
|
||||
|
||||
proc createFdSet(fd: var TFdSet, s: seq[TSocket], m: var int) =
|
||||
FD_ZERO(fd)
|
||||
|
|
@ -1608,14 +1655,14 @@ when defined(Windows):
|
|||
FIONBIO = IOC_IN.int32 or ((sizeof(int32) and IOCPARM_MASK) shl 16) or
|
||||
(102 shl 8) or 126
|
||||
|
||||
proc ioctlsocket(s: TWinSocket, cmd: clong,
|
||||
proc ioctlsocket(s: TSocketHandle, cmd: clong,
|
||||
argptr: ptr clong): cint {.
|
||||
stdcall, importc:"ioctlsocket", dynlib: "ws2_32.dll".}
|
||||
|
||||
proc setBlocking(s: TSocket, blocking: bool) =
|
||||
when defined(Windows):
|
||||
var mode = clong(ord(not blocking)) # 1 for non-blocking, 0 for blocking
|
||||
if ioctlsocket(TWinSocket(s.fd), FIONBIO, addr(mode)) == -1:
|
||||
if ioctlsocket(TSocketHandle(s.fd), FIONBIO, addr(mode)) == -1:
|
||||
OSError(OSLastError())
|
||||
else: # BSD sockets
|
||||
var x: int = fcntl(s.fd, F_GETFL, 0)
|
||||
|
|
@ -1656,9 +1703,12 @@ proc connect*(socket: TSocket, address: string, port = TPort(0), timeout: int,
|
|||
proc isSSL*(socket: TSocket): bool = return socket.isSSL
|
||||
## Determines whether ``socket`` is a SSL socket.
|
||||
|
||||
proc getFD*(socket: TSocket): cint = return socket.fd
|
||||
proc getFD*(socket: TSocket): TSocketHandle = return socket.fd
|
||||
## Returns the socket's file descriptor
|
||||
|
||||
proc isBlocking*(socket: TSocket): bool = not socket.nonblocking
|
||||
## Determines whether ``socket`` is blocking.
|
||||
|
||||
when defined(Windows):
|
||||
var wsa: TWSADATA
|
||||
if WSAStartup(0x0101'i16, addr wsa) != 0: OSError(OSLastError())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue