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

chore(cbindings): Thread-safe libwaku. WakuNode instance created directly from the Waku Thread #1957

Merged
merged 7 commits into from
Sep 1, 2023

Conversation

Ivansete-status
Copy link
Collaborator

@Ivansete-status Ivansete-status commented Aug 28, 2023

Description

This is aimed at having a safer c-bindings libwaku. With that regard, given that the WakuNode is a GC-managed type, we need to make sure the instance of the WakuNode is directly created from within the Waku Thread itself.

note: each ref, seq[T], or string are GC-manged types

Changes

  • library/waku_thread/inter_thread_communication/node_lifecycle_request.nim -> New CREATE_NODE operation type.
  • library/libwaku.nim -> Splitting the Waku Node creation in two parts: 1) create the Waku Thread. 2) send a CREATE_NODE req to the Waku Thread so that it creates the Waku Node instance.
  • examples/cbindings/waku_example.c -> fix so that make cwaku_example actually compiles.

Issue

#1878

@Ivansete-status Ivansete-status self-assigned this Aug 28, 2023
@Ivansete-status Ivansete-status changed the title chore(cbindings): Thread-safe libwaku. _Waku Node_ instance created directly from the _Waku Thread_ chore(cbindings): Thread-safe libwaku. WakuNode instance created directly from the Waku Thread Aug 28, 2023
builder.withRecord(record)
builder.withNetworkConfiguration(netConfig)
builder.withSwitchConfiguration(
maxConnections = some(50.int)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be really hardcoded? Can you at least make it a constant?

if newNodeRes.isErr():
return err(newNodeRes.error)

node[] = newNodeRes.get()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this notation (the []) mean/do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comments! I'll fix them asap.
This [] is used for ptr types. I understand the [] operator as a way to dereference the ptr, i.e. a way to access the value or object it points to.

Something interesting I've learned recently:

ref obj A ptr A
Points to data stored in the heap Points to data stored either in the heap or stack
GC'ed The developer should take care of allocating and deallocating manually
No thread-safe Thread-safe

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, [] is also used to dereference ref - in this sense, ref and ptr is similar - they differ in that ref is a reference-counted pointer and ptr is a "manual" pointer - for example:

proc myFunction(v: int) = echo v



var a = new int # this returns a `ref int`
let b = addr a[] # this returns a `ptr int` pointing to the same memory location as `a`

myFunction(a[]) # call the function with a dereferenced value
myFunction(b[])

a = nil # this removes the reference that `a` was holding - `b` now points to a memory location that the GC will reclaim - it's a dangling pointer

nim "sometimes" dereferences ref automatically as part of the language semantics - I find this to mostly be a misfeature that leads to more problems than it solves -> I always put [] when using either of ptr and ref (to remind myself that both could be nil for example)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much!

library/alloc.nim Outdated Show resolved Hide resolved
Comment on lines 76 to 80
let createThRes = waku_thread.createWakuThread()
if createThRes.isErr():
let msg = "Error in createWakuThread: " & $createThRes.error
onErrCb(unsafeAddr msg[0], cast[csize_t](len(msg)))
return RET_ERR
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let createThRes = waku_thread.createWakuThread()
if createThRes.isErr():
let msg = "Error in createWakuThread: " & $createThRes.error
onErrCb(unsafeAddr msg[0], cast[csize_t](len(msg)))
return RET_ERR
let createTh = waku_thread.createWakuThread().valueOr:
let msg = "Error in createWakuThread: " & $error
onErrCb(unsafeAddr msg[0], cast[csize_t](len(msg)))
return RET_ERR

Copy link
Contributor

@arnetheduck arnetheduck Aug 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or.. if this is Result[void, E], use isOkOr:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super interesting! Thanks for that! Notice that I will only apply that to this snippet and I'm planning to extend this approach to further places at nwaku.


of START_NODE:
waitFor node.start()
waitFor node[].start()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{.async.} functions must never call waitFor - instead, they should use await

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yes, my bad!


method process*(self: NodeLifecycleRequest,
node: WakuNode): Future[Result[string, string]] {.async.} =
node: ptr WakuNode): Future[Result[string, string]] {.async.} =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WakuNode is a ref object - the previous code without the ptr was correct - a consequence is that process is not a "thread-safe" function - it can only be called from the thread that created the WakuNode instance (ie the processing loop below)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, I see now - this looks as if you wanted to return the node instance and async prevents var from being used - ok, ptr can be used as a workaround for that limitation as long as you keep in mind that a ptr might become dangling - this is a bit of a general problem here and for this PR it's an ok tradeoff to improve the status quo - later, I would probably look for solutions that avoid this quirk

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the idea of using ptr was to alter the node instance:

var node: WakuNode

This instance will live enough for all the calls to any process proc and will always be owned by the Waku Thread.

Copy link
Member

@vpavlin vpavlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@Ivansete-status
Copy link
Collaborator Author

I will merge this as we have another PR that is blocked by that one.
Thank you so much indeed for the comments guys!
@arnetheduck - Although the PR will get merged, I can revisit any further suggestions that may arise. Thank you very much for the comments so far. I'll set you as a reviewer for the upcoming PR, where we avoid the use of the Channel type.

@Ivansete-status Ivansete-status merged commit 68e8d9a into master Sep 1, 2023
11 checks passed
@Ivansete-status Ivansete-status deleted the thread-safe-libwaku branch September 1, 2023 06:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants