Skip to content

Commit 63fee7e

Browse files
committed
use uniqueptr for sharing data across threads
1 parent 00a0381 commit 63fee7e

File tree

3 files changed

+79
-19
lines changed

3 files changed

+79
-19
lines changed

codex/erasure/erasure.nim

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import ../utils/asynciter
3232
import ../indexingstrategy
3333
import ../errors
3434
import ../utils/arrayutils
35+
import ../utils/uniqueptr
3536

3637
import pkg/stew/byteutils
3738

@@ -98,7 +99,7 @@ type
9899
success: Atomic[bool]
99100
erasure: ptr Erasure
100101
blocks: seq[seq[byte]]
101-
parity: Isolated[seq[seq[byte]]]
102+
parity: UniquePtr[seq[seq[byte]]]
102103
blockSize, parityLen: int
103104
signal: ThreadSignalPtr
104105

@@ -107,7 +108,7 @@ type
107108
erasure: ptr Erasure
108109
blocks: seq[seq[byte]]
109110
parity: seq[seq[byte]]
110-
recovered: Isolated[seq[seq[byte]]]
111+
recovered: UniquePtr[seq[seq[byte]]]
111112
blockSize, recoveredLen: int
112113
signal: ThreadSignalPtr
113114

@@ -311,8 +312,12 @@ proc leopardEncodeTask(tp: Taskpool, task: ptr EncodeTask) {.gcsafe.} =
311312

312313
task[].success.store(false)
313314
else:
314-
var isolatedParity = isolate(parity)
315-
task[].parity = move isolatedParity
315+
var isolatedSeq = newSeq[seq[byte]](task[].parityLen)
316+
for i in 0 ..< task[].parityLen:
317+
var innerSeq = isolate(parity[i])
318+
isolatedSeq[i] = extract(innerSeq)
319+
320+
task[].parity = newUniquePtr(isolatedSeq)
316321
task[].success.store(true)
317322

318323
proc asyncEncode*(
@@ -349,12 +354,7 @@ proc asyncEncode*(
349354
if not task.success.load():
350355
return failure("Leopard encoding task failed")
351356

352-
defer:
353-
task.parity = default(Isolated[seq[seq[byte]]])
354-
355-
var parity = task.parity.extract
356-
357-
success parity
357+
success extractValue(task.parity)
358358

359359
proc encodeData(
360360
self: ErasureRef, manifest: Manifest, params: EncodingParams
@@ -474,8 +474,12 @@ proc leopardDecodeTask(tp: Taskpool, task: ptr DecodeTask) {.gcsafe.} =
474474
warn "Error from leopard decoder backend!", error = $res.error
475475
task[].success.store(false)
476476
else:
477-
var isolatedRecovered = isolate(recovered)
478-
task[].recovered = move isolatedRecovered
477+
var isolatedSeq = newSeq[seq[byte]](task[].blocks.len)
478+
for i in 0 ..< task[].blocks.len:
479+
var innerSeq = isolate(recovered[i])
480+
isolatedSeq[i] = extract(innerSeq)
481+
482+
task[].recovered = newUniquePtr(isolatedSeq)
479483
task[].success.store(true)
480484

481485
proc asyncDecode*(
@@ -509,15 +513,10 @@ proc asyncDecode*(
509513

510514
return failure(err)
511515

512-
defer:
513-
task.recovered = default(Isolated[seq[seq[byte]]])
514-
515516
if not task.success.load():
516517
return failure("Leopard decoding task failed")
517518

518-
var recovered = task.recovered.extract
519-
520-
success(recovered)
519+
success extractValue(task.recovered)
521520

522521
proc decodeInternal(
523522
self: ErasureRef, encoded: Manifest

codex/utils/uniqueptr.nim

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import std/isolation
2+
type UniquePtr*[T] = object
3+
## A unique pointer to a seq[seq[T]] in shared memory
4+
## Can only be moved, not copied
5+
data: ptr T
6+
7+
template newUniquePtr*[T](data: T): UniquePtr[T] =
8+
newUniquePtr(isolate(data))
9+
10+
proc newUniquePtr*[T](data: sink Isolated[T]): UniquePtr[T] =
11+
## Creates a new unique sequence in shared memory
12+
## The memory is automatically freed when the object is destroyed
13+
result.data = cast[ptr T](allocShared0(sizeof(T)))
14+
15+
result.data[] = extract(data)
16+
17+
proc `=destroy`*[T](p: var UniquePtr[T]) =
18+
## Destructor for UniquePtr
19+
if p.data != nil:
20+
deallocShared(p.data)
21+
p.data = nil
22+
23+
proc `=copy`*[T](
24+
dest: var UniquePtr[T], src: UniquePtr[T]
25+
) {.error: "UniquePtr cannot be copied, only moved".}
26+
27+
proc `=sink`*[T](dest: var UniquePtr[T], src: UniquePtr[T]) =
28+
if dest.data != nil:
29+
`=destroy`(dest)
30+
dest.data = src.data
31+
# We need to nil out the source data to prevent double-free
32+
# This is handled by Nim's destructive move semantics
33+
34+
proc `[]`*[T](p: UniquePtr[T]): lent T =
35+
## Access the data (read-only)
36+
if p.data == nil:
37+
raise newException(NilAccessDefect, "accessing nil UniquePtr")
38+
p.data[]
39+
40+
# proc `[]`*[T](p: var UniquePtr[T]): var T =
41+
# ## Access the data (mutable)
42+
# if p.data == nil:
43+
# raise newException(NilAccessDefect, "accessing nil UniquePtr")
44+
# p.data[]
45+
46+
proc isNil*[T](p: UniquePtr[T]): bool =
47+
## Check if the UniquePtr is nil
48+
p.data == nil
49+
50+
proc extractValue*[T](p: var UniquePtr[T]): T =
51+
## Extract the value from the UniquePtr and release the memory
52+
if p.data == nil:
53+
raise newException(NilAccessDefect, "extracting from nil UniquePtr")
54+
55+
# Move the value out
56+
var isolated = isolate(p.data[])
57+
result = extract(isolated)
58+
59+
# Free the shared memory
60+
deallocShared(p.data)
61+
p.data = nil

vendor/nim-leopard

0 commit comments

Comments
 (0)