open_nonblock(3)

πŸ“¦
version 0.7.1 Β· 2 versions
, in nixos 26.05

πŸ’– open_nonblock(3) β€” the Arcan Lua API glow-up πŸ’–

okay so bestie, we're in the Arcan Lua API and this page is literally your new fave: open_nonblock, the one that opens files in read or write mode for non-blocking I/O ✨ grab a strawberry drink, we're going in πŸ“


🌸 NAME 🌸

open_nonblock β€” open a file in read or write mode for non-blocking I/O. that's it, that's the whole vibe πŸ’…


✨ SYNOPSIS ✨

like, here are all the forms, no cap, all of them:

blocktbl
open_nonblock( vid:res )
open_nonblock( vid:res, bool:write )
open_nonblock( vid:res, bool:write, string:identifier=stream )
open_nonblock( vid:res, bool:write, string:identifier=stream,
userdata:nbioud )
open_nonblock( vid:res, table:opts, string:identifier=stream )
open_nonblock( vid:res, table:opts, string:identifier=stream,
userdata:nbioud )
open_nonblock( string:res )
open_nonblock( string:res, bool:write )

πŸ¦‹ DESCRIPTION πŸ¦‹

so what it does: it creates or opens the resource that res points at β€” in read mode by default, or in write mode if a mode is provided. iconic behaviour, honestly 🫢

if res is a vid connected to a frameserver, one of two things happens: either a streaming fifo session gets set up over the connection, along with the matching _BCHUNK events, or a pre-existing nonblock-io stream (nbioud) gets redirected to that client and the backing descriptor is closed locally. πŸ”₯

then the identifier argument is your moment to specify some client-announced type identifier, or one of the reserved ones: "stdin", "stdout", "stderr". yup, the classics πŸ‘‘

if res is a string, the very first character decides what gets made: a FIFO (<) or a SOCKET (=). and unless a namespace is explicitly set and that namespace is marked as valid for IPC, both FIFOs and SOCKETs get created in the RESOURCE_APPL_TEMP namespace. for sockets, the write argument decides the direction: outbound when it's =true, listening when it's =false. pick your fighter πŸ’€

if the identifier starts with a valid namespace identifier plus separator (alphanum:/), that identifier gets matched against a user-defined namespace first β€” see list_namespaces. iykyk πŸ’œ

and if res is connected to a frameserver, the namespace identifier is fixed and only defined for connections coming from net_open. the default is to target the private store of the authenticated client. to let the server-side controller script decide instead, prefix the identifier with appl:/. main character energy, fr fr ✨

if it all works out: FIFOs and normal resources return a table with a close operation (which gets activated on garbage collection unless you called it in beforehand, just saying πŸ™ˆ) plus a read or write function depending on the mode the resource was opened in. slay.

the socket table is special though β€” it allows multiple connections 🌈 the initial table for a socket only has a close and an accept function. accept takes no arguments and returns a table in both read and write mode when there's a client waiting. obsessed πŸŽ€

oh and the opts table argument form? that one's for tuning transfer parameters. cute little knobs, love that for us.


🧚 NOTES 🧚

  1. do note that input processing has soft realtime constraints β€” so be careful and don't process large chunks of data in one go, because it may affect application responsiveness. not me warning you lovingly 😭
  2. FIFOs and sockets that were created will be unlinked when the close method is called, or when the table reference gets garbage collected. it's giving cleanup era.
  3. when a nonblocking userdata is handed over to a frameserver, the write argument will be ignored and the corresponding BCHUNK event will match the userdata. this was a bug in a previous version, where the write argument was respected and the argument had to be kept to avoid breaking compatibility. legacy lore πŸ’€

πŸŽ€ EXAMPLE πŸŽ€

function open_nonblock0()
      a = open_nonblock("test.txt")
      if (a == nil) then
            return shutdown("couldn't open test.txt");
      end
end
function main_clock_pulse()
      local line = a:read();
      if (line ~= nil) then
            print(line);
      end
end

⭐ EXAMPLE ⭐

function open_nonblock1()
      a = open_nonblock("=test", false)
      b = open_nonblock("=test", true)
-- our reader, note the scoping and the closure here
      local counter = 20
      local dh
      dh =
      function()
            local res, alive = a:read()
            if res then
                  print("read:", res)
                  counter = counter - 1
                  if alive and counter > 0 then
                        a:data_handler(dh)
                        return
                  end
            end
            a:close() -- will close on error or if counter reaches 0
      end
      a:data_handler(dh)
end
function main_clock_pulse()
      if CLOCK % 100 == 0 then
            local _, alive = b:write("sent at " .. tostring(CLOCK))
            if not alive then
                  return shutdown()
            end
      end
end

🌈 SEE ALSO 🌈

resource β€” go read that one too bestie, it's the lore 🫢


πŸ’– that's the whole page! brought to you by the source/version resource, dated October 2026 β€” mwah, see you in the next manual, bestie πŸ’…βœ¨πŸŽ€
πŸ“¦ where this page comes from