Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom deleters for smart pointers #80

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 73 additions & 21 deletions threading/smartptrs.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,26 @@ template checkNotNil(p: typed) =
if p.isNil:
raiseNilAccess()

# mimic deallocShared signature
type Deleter = proc(p: pointer) {.noconv, raises: [], gcsafe.}

type
UniquePtr*[T] = object
## Non copyable pointer to a value of type `T` with exclusive ownership.
val: ptr T
memalloc: pointer
deleter: Deleter

when defined(nimAllowNonVarDestructor):
proc `=destroy`*[T](p: UniquePtr[T]) =
if p.val != nil:
`=destroy`(p.val[])
deallocShared(p.val)
p.deleter(p.memalloc)
else:
proc `=destroy`*[T](p: var UniquePtr[T]) =
if p.val != nil:
`=destroy`(p.val[])
deallocShared(p.val)
p.deleter(p.memalloc)

proc `=dup`*[T](src: UniquePtr[T]): UniquePtr[T] {.error.}
## The dup operation is disallowed for `UniquePtr`, it
Expand All @@ -51,6 +56,8 @@ proc newUniquePtr*[T](val: sink Isolated[T]): UniquePtr[T] {.nodestroy.} =
# here either.
result.val[] = extract val
# no destructor call for 'val: sink T' here either.
result.memalloc = cast[pointer](result.val)
result.deleter = deallocShared

template newUniquePtr*[T](val: T): UniquePtr[T] =
newUniquePtr(isolate(val))
Expand All @@ -62,6 +69,18 @@ proc newUniquePtr*[T](t: typedesc[T]): UniquePtr[T] =
result.val = cast[ptr T](allocShared0(sizeof(T)))
else:
result.val = cast[ptr T](allocShared(sizeof(T)))
result.memalloc = cast[pointer](result.val)
result.deleter = deallocShared

proc wrapUniquePtr*[T](p: ptr T, memalloc: pointer, deleter: Deleter): UniquePtr[T] =
## Returns a unique pointer that wraps a raw pointer.
## On destruction calls deleter on memalloc.
result.val = p
result.memalloc = memalloc
result.deleter = deleter

proc get*[T](p: UniquePtr[T]): ptr T {.inline.} =
p.val

proc isNil*[T](p: UniquePtr[T]): bool {.inline.} =
p.val == nil
Expand All @@ -87,15 +106,19 @@ proc `$`*[T](p: UniquePtr[T]): string {.inline.} =
type
SharedPtr*[T] = object
## Shared ownership reference counting pointer.
val: ptr tuple[value: T, counter: Atomic[int]]
val: ptr T
ctx: ptr tuple[counter: Atomic[int], memalloc: pointer, deleter: Deleter]

template frees(p) =
if p.val != nil:
if p.ctx != nil:
# this `fetchSub` returns current val then subs
# so count == 0 means we're the last
if p.val.counter.fetchSub(1, moAcquireRelease) == 0:
`=destroy`(p.val.value)
deallocShared(p.val)
if p.ctx.counter.fetchSub(1, moAcquireRelease) == 0:
if p.val != nil:
`=destroy`(p.val[])
p.ctx.deleter(p.ctx.memalloc)
`=destroy`(p.ctx[])
deallocShared(p.ctx)

when defined(nimAllowNonVarDestructor):
proc `=destroy`*[T](p: SharedPtr[T]) =
Expand All @@ -106,24 +129,30 @@ else:

proc `=wasMoved`*[T](p: var SharedPtr[T]) =
p.val = nil
p.ctx = nil

proc `=dup`*[T](src: SharedPtr[T]): SharedPtr[T] =
if src.val != nil:
discard fetchAdd(src.val.counter, 1, moRelaxed)
if src.ctx != nil:
discard fetchAdd(src.ctx.counter, 1, moRelaxed)
result.val = src.val
result.ctx = src.ctx

proc `=copy`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) =
if src.val != nil:
discard fetchAdd(src.val.counter, 1, moRelaxed)
if src.ctx != nil:
discard fetchAdd(src.ctx.counter, 1, moRelaxed)
`=destroy`(dest)
dest.val = src.val
dest.ctx = src.ctx

proc newSharedPtr*[T](val: sink Isolated[T]): SharedPtr[T] {.nodestroy.} =
## Returns a shared pointer which shares
## ownership of the object by reference counting.
result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[])))
result.val.counter.store(0, moRelaxed)
result.val.value = extract val
result.val = cast[ptr T](allocShared(sizeof(T)))
result.ctx = cast[typeof(result.ctx)](allocShared(sizeof(result.ctx[])))
result.ctx.counter.store(0, moRelaxed)
result.ctx.memalloc = result.val
result.ctx.deleter = deallocShared
result.val[] = extract val

template newSharedPtr*[T](val: T): SharedPtr[T] =
newSharedPtr(isolate(val))
Expand All @@ -132,28 +161,43 @@ proc newSharedPtr*[T](t: typedesc[T]): SharedPtr[T] =
## Returns a shared pointer. It is not initialized,
## so reading from it before writing to it is undefined behaviour!
when not supportsCopyMem(T):
result.val = cast[typeof(result.val)](allocShared0(sizeof(result.val[])))
result.val = cast[ptr T](allocShared0(sizeof(T)))
else:
result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[])))
result.val.counter.store(0, moRelaxed)
result.val = cast[ptr T](allocShared(sizeof(T)))
result.ctx = cast[typeof(result.ctx)](allocShared(sizeof(result.ctx[])))
result.ctx.counter.store(0, moRelaxed)
result.ctx.memalloc = result.val
result.ctx.deleter = deallocShared

proc wrapSharedPtr*[T](p: ptr T, memalloc: pointer, deleter: Deleter): SharedPtr[T] =
## Returns a shared pointer that wraps a raw pointer.
## On destruction calls deleter on memalloc.
result.val = p
result.ctx = cast[typeof(result.ctx)](allocShared(sizeof(result.ctx[])))
result.ctx.counter.store(0, moRelaxed)
result.ctx.memalloc = memalloc
result.ctx.deleter = deleter

proc get*[T](p: SharedPtr[T]): ptr T {.inline.} =
p.val

proc isNil*[T](p: SharedPtr[T]): bool {.inline.} =
p.val == nil

proc `[]`*[T](p: SharedPtr[T]): var T {.inline.} =
checkNotNil(p)
p.val.value
p.val[]

proc `[]=`*[T](p: SharedPtr[T], val: sink Isolated[T]) {.inline.} =
checkNotNil(p)
p.val.value = extract val
p.val[] = extract val

template `[]=`*[T](p: SharedPtr[T]; val: T) =
`[]=`(p, isolate(val))

proc `$`*[T](p: SharedPtr[T]): string {.inline.} =
if p.val == nil: "nil"
else: "(val: " & $p.val.value & ")"
else: "(val: " & $p.val[] & ")"

#------------------------------------------------------------------------------

Expand All @@ -168,13 +212,21 @@ proc newConstPtr*[T](val: sink Isolated[T]): ConstPtr[T] {.nodestroy, inline.} =
template newConstPtr*[T](val: T): ConstPtr[T] =
newConstPtr(isolate(val))

proc wrapConstPtr*[T](p: ptr T, memalloc: pointer, deleter: Deleter): ConstPtr[T] {.inline.} =
## Returns a const pointer that wraps a raw pointer.
## On destruction calls deleter on memalloc.
ConstPtr[T](wrapSharedPtr(p, memalloc, deleter))

proc get*[T](p: ConstPtr[T]): ptr T {.inline.} =
return p.val

proc isNil*[T](p: ConstPtr[T]): bool {.inline.} =
SharedPtr[T](p).val == nil

proc `[]`*[T](p: ConstPtr[T]): lent T {.inline.} =
## Returns an immutable view of the internal value of `p`.
checkNotNil(p)
SharedPtr[T](p).val.value
SharedPtr[T](p).val[]

proc `[]=`*[T](p: ConstPtr[T], v: T) {.error: "`ConstPtr` cannot be assigned.".}

Expand Down
Loading