Here's a simple example of a thread that starts another thread:
parent.hoon
/- spider
/+ *strandio
=, strand=strand:spider
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
;< tid=tid:spider bind:m (start-thread %child)
(pure:m !>(~))
child.hoon
/- spider
/+ *strandio
=, strand=strand:spider
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
%- (slog leaf+"foo" ~)
(pure:m !>(~))
Save parent.hoon
and child.hoon
in /ted
of the %base
desk, |commit %base
and run -parent
. You should see something like:
foo
> -parent
~
parent.hoon
just uses the strandio
function start-thread
to start child.hoon
, and child.hoon
just prints foo
to the dojo. Since we got foo
we can tell the second thread did, in fact, run.
;< tid=tid:spider bind:m (start-thread %child)
See here how we gave start-thread
the name of the thread to run. It returns the tid
of the thread, which we could then use to poke it or whatever.
start-thread
handles creating the tid
for the thread so is quite convenient.
Note that threads we start this way will be a child of the thread that started them, and so will be killed when the parent thread ends.
Start thread and get its result
If we want to actually get the result of the thread we started, it's slightly more complicated.
We note that this is mostly the same as await-thread:strandio
.
parent.hoon
/- spider
/+ *strandio
=, strand=strand:spider
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
;< =bowl:spider bind:m get-bowl
=/ tid `@ta`(cat 3 'strand_' (scot %uv (sham %child eny.bowl)))
;< ~ bind:m (watch-our /awaiting/[tid] %spider /thread-result/[tid])
;< ~ bind:m %- poke-our
:* %spider
%spider-start
!>([`tid.bowl `tid byk.bowl(r da+now.bowl) %child !>(~)])
==
;< =cage bind:m (take-fact /awaiting/[tid])
;< ~ bind:m (take-kick /awaiting/[tid])
?+ p.cage ~|([%strange-thread-result p.cage %child tid] !!)
%thread-done (pure:m q.cage)
%thread-fail (strand-fail !<([term tang] q.cage))
==
child.hoon
/- spider
/+ *strandio
=, strand=strand:spider
=>
|%
++ url "https://www.whatsthelatestbasehash.com/"
--
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
;< =cord bind:m (fetch-cord url)
=/ hash-as-cord `@t`(end [3 (sub (met 3 cord) 1)] cord)
=/ hash `@uv`(slav %uv hash-as-cord)
(pure:m !>(hash))
child.hoon
simply grabs the latest base hash from https://www.whatsthelatestbasehash.com/ and returns it.
parent.hoon
is a bit more complicated so we'll look at it line-by-line
;< =bowl:spider bind:m get-bowl
First we grab the bowl
=/ tid `@ta`(cat 3 'strand_' (scot %uv (sham %child eny.bowl)))
Then we generate a tid
(thread ID) for the thread we're gonna start
;< ~ bind:m (watch-our /awaiting/[tid] %spider /thread-result/[tid])
We pre-emptively subscribe for the result. Spider sends the result at /thread-result/
so that's where we subscribe.
;< ~ bind:m %- poke-our
:* %spider
%spider-start
!>([`tid.bowl `tid byk.bowl(r da+now.bowl) %child !>(~)])
==
Spider takes a poke with a mark %spider-start and a vase containing [parent=(unit tid) use=(unit tid) =beak file=term =vase]
to start a thread, where:
parent
is an optional parent thread. In this case we say the parent is our tid. Specifying a parent means the child will be killed if the parent ends.use
is the thread ID for the thread we're creatingbeak
is a[p=ship q=desk r=case]
triple which specifies the desk and revision containing the thread we want to run. In this case we just usebyk.bowl
, but with the date of revisionq
changed tonow.bowl
.file
is the filename of the thread we want to startvase
is the vase it will be given as an argument when it's started
;< =cage bind:m (take-fact /awaiting/[tid])
We wait for a fact which will be the result of the thread.
;< ~ bind:m (take-kick /awaiting/[tid])
Spider will kick us from the subscription when it ends the thread so we also take that kick.
?+ p.cage ~|([%strange-thread-result p.cage %child tid] !!)
%thread-done (pure:m q.cage)
%thread-fail (strand-fail !<([term tang] q.cage))
==
Finally we test whether the thread produced a %thread-done
or a %thread-fail
. These are the two possible marks produced by spider when it returns the results of a thread. A %thread-done
will contain a vase with the result, and a %thread-fail
will contain an error message and traceback, so we see which it is and then either produce the result with pure
or trigger a %thread-fail
with the error we got from the child.
Stop a thread
parent.hoon
/- spider
/+ *strandio
=, strand=strand:spider
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
;< =bowl:spider bind:m get-bowl
=/ tid `@ta`(cat 3 'strand_' (scot %uv (sham %child eny.bowl)))
%- (slog leaf+"Starting child thread..." ~)
;< ~ bind:m %- poke-our
:* %spider
%spider-start
!>([`tid.bowl byk.bowl(r da+now.bowl) `tid %child !>(~)])
==
;< ~ bind:m (sleep ~s5)
%- (slog leaf+"Stopping child thread..." ~)
;< ~ bind:m %- poke-our
:* %spider
%spider-stop
!>([tid %.y])
==
;< ~ bind:m (sleep ~s2)
(pure:m !>("Done"))
child.hoon
/- spider
/+ *strandio
=, strand=strand:spider
=>
|%
++ looper
=/ m (strand ,~)
^- form:m
%- (main-loop ,~)
:~ |= ~
^- form:m
;< ~ bind:m (sleep `@dr`(div ~s1 2))
%- (slog leaf+"child thread" ~)
(pure:m ~)
==
--
^- thread:spider
|= arg=vase
=/ m (strand ,vase)
^- form:m
;< ~ bind:m looper
(pure:m !>(~))
child.hoon
just prints to the dojo in a loop.
parent.hoon
starts child.hoon
, and then pokes spider like:
;< ~ bind:m %- poke-our
:* %spider
%spider-stop
!>([tid %.y])
==
%spider-stop
is the mark that tells spider to kill a thread.tid
is the tid of the thread to kill%.y
tells spider to suppress traceback in the result of the killed thread. If you give it%.n
it will include the traceback.