Table of Contents

Thread API

The Thread API provides a variation of coroutines for openos. A thread is superior to basic coroutines in many ways and, for many workflows, is easier to work with. An openos thread is an autonomous non-blocking detachable process.

Event Registration Independence

A thread maintains an independent set of event registrations; it does not inherit any and it does not share any. Any event registration made (e.g. listeners or timers) inside a thread belongs to that thread.

Overview

There are two main use cases for using threads over other viable options

Functions

There are two sections of API defined here.

  1. The thread api, or the static functions, provided by require("thread")
  2. The thread handle api, or the api available the thread objects created by thread.create. In this documentation these thread handles will be denoted by just t.

Thread API

snippet.lua
local thread = require("thread")
print("Main program start")
thread.create(function(a, b)
  print(a)
  os.sleep()
  print(b)
end, 'A', 'B')
print("Main program end")

Output:

Main program start
A
Main program end
B
snippet.lua
local thread = require("thread")
print("Main program start")
local t = thread.create(function(a, b)
  print(a)
  os.sleep()
  print(b)
end, 'A', 'B')
thread.waitForAll({t})
print("Main program end")

Output:

Main program start
A
B
Main program end
snippet.lua
local thread = require("thread")
print("Main program start")
local t1 = thread.create(function(a, b)
  print(a)
  os.sleep()
  print(b)
end, 'A', 'B')
local t2 = thread.create(function(c, d)
  print(c)
  os.sleep()
  os.sleep()
  print(d)
end, 'C', 'D')
thread.waitForAny({t1, t2})
print("Main program end")

Output:

Main program start
A
C
B
Main program end
D

Please note that threads resume order is not specified and this example may print “D” before it prints “Main program end”

Thread Handle API

Special notes about t:resume, t:suspend

Do not think of these methods as coroutine.resume() nor coroutine.yield(). These methods are indirect and a thread will asynchronously start or stop running on its own. Contrast this to coroutine methods which directly and immediately invoke execution or leave execution of a coroutine. Consider these examples:

snippet.lua
local thread = require("thread")
local t -- this example needs an upvalue to t
t = thread.create(function()
  print("start")
  thread.current():suspend()
  print("after suspend")
  os.sleep()
  print("after sleep")
end)

Output:

start
snippet.lua
local thread = require("thread")
local t -- this example needs an upvalue to t
t = thread.create(function()
  print("start")
  thread.current():suspend()
  print("after suspend")
  os.sleep()
  print("after sleep")
end)
print("outside thread create")
t:resume()
print("after resume")

Output:

start
outside thread create
after suspend
after resume
after sleep
snippet.lua
local thread = require("thread")
local t = thread.create(function()
  while true do
    print("running")
    print("still running")
    os.sleep()
    print("after sleep")
  end
  print("unreachable code")
end)
print("before kill")
t:kill()
print("after kill")

Output:

running
still running
before kill
after kill

Status Examples

snippet.lua
local thread = require("thread")
local t = thread.create(function()
  print("before sleep")
  os.sleep()
  print("after sleep")
end)
print(t:status())

Output:

before sleep
running
after sleep
snippet.lua
local thread = require("thread")
local t = thread.create(function()
  print("before sleep")
  os.sleep()
  print("after sleep")
end)
t:suspend()
print(t:status())
os.sleep(10)
print(t:status())
t:resume()
print("after resume")
print(t:status())

Output:

before sleep
suspended
suspended
after resume
dead
snippet.lua
local detached_thread = thread.create(function() end):detach()

Thread Exception Example

This example demonstrates what happens when a thread throws an exception. A thread stops executing and becomes “dead” when it throws an uncaught exception. The exception message is not printed to stdout nor stderr (see t:resume()) for details.

snippet.lua
local thread = require("thread")
local tty = require("tty")
 
print("p start")
local reader = thread.create(function()
  print("reader start")
  error("thread abort") -- throws an exception
  print("reader done")
end)
print("p end", reader:status())

Output

p start
reader start
p end	dead

Thread Interrupt Handler Example

This example demonstrates how you would register a function that handles soft interrupts (^c) to close file handles, release resources, etc, and then exit the whole program.

snippet.lua
local thread = require("thread")
local event = require("event")
 
local cleanup_thread = thread.create(function()
  event.pull("interrupted")
  print("cleaning up resources")
end)
 
local main_thread = thread.create(function()
  print("main program")
  while true do
    io.write("input: ")
    io.read()
  end
end)
 
thread.waitForAny({cleanup_thread, main_thread})
os.exit(0) -- closes all remaining threads

Assuming the user presses ^c to send an interrupt Output

main program
input: ^c
cleaning up resources

Thread Yield/Pull Without Blocking Example

This example demonstrates that now OpenOS supports non blocking threads.

snippet.lua
local event = require("event")
local thread = require("thread")
thread.create(function()
  a,b,c,d,e,f,g = coroutine.yield()
  print(a,b,c,d,e,f,g)
  print(event.pull())
end)
 
event.push("custom_event_a")
print("done")
event.push("custom_event_b", 2)

Output

custom_event_a
done
custom_event_b	2

Contents