Flag

A lock for the threads of one program: whoever asks first gets it first.

RunAsync(work) holds the flag while work runs and lets go when it returns or errors, so it is the safe way in. PickupAsync hands you a FlagHolder to release yourself, or nil once the timeout passes. Make one with Flag.new().

Example

Purchases taking turnsLuau
--!strict
-- Two purchases that wait in the middle of an update take turns on one Flag, first come first served.
local Flag = require("@shotsystem/Flag")
local Promise = require("@shotsystem/Promise")
local TimeService = require("@shotsystem/TimeService")

local wallet = { Coins = 100 }
local flag = Flag.new()
-- reads the coins, waits (as a save would), then writes them back
local function buy(item : string, price : number)
	flag:RunAsync(function()
		local coins = wallet.Coins
		TimeService:WaitAsync(0.05)
		wallet.Coins = coins - price
		print(item, "bought, coins left:", wallet.Coins)
	end)
end

local apple = Promise.Spawn(buy, "Apple", 30)
local pear = Promise.Spawn(buy, "Pear", 20)
print("while they run:", tostring(flag), flag.Taken)
Promise.AllAsync({ apple, pear })
print("coins:", wallet.Coins, tostring(flag), flag.Taken)

-- RunAsync gives back what the work returns, and lets go even when the work errors
print("result:", flag:RunAsync(function() return wallet.Coins, "coins" end))
print("error:", pcall(function()
	flag:RunAsync(function() error("no stock", 0) end)
end))
print("taken after the error:", flag.Taken)
What it printedOutput
while they run: Flag(taken) true
Apple bought, coins left: 70
Pear bought, coins left: 50
coins: 50 Flag(free) false
result: 50 coins
error: false no stock
taken after the error: false

Constructors

NameArgumentsReturnsDescription
Flag.new()Flag

Properties

NameTypeDefaultDescription
TakenbooleanWhether a thread holds the flag now; false again after Release, and after RunAsync's work returns or errors.

Methods

NameArgumentsReturnsDescription
PickupAsync
yields
(timeout: number?)FlagHolder?Waits its turn, in arrival order, for the flag and gives the FlagHolder, or nil once the timeout passes.
RunAsync
yields
(work: () -> ...any)...anyWaits for the flag, runs work() holding it and gives back all its results; the flag is released even when work errors.