new API for file operations
File operations now always return a channel (or nil on error or permission denied). When start_reading() from a filename, you can repeatedly :recv() from the channel it returns. When :recv() returns nil, you're at the end of the file. Stop. When you start_writing() to a filename, you can repeatedly :send() to the channel it returns. When you're done writing, :close() the channel. Writes to the file won't be externally visible until you do. To make this work I'm now always starting up the scheduler, so I need to fix sieve.tlv. Transparently running the scheduler is an abstraction, and whenever I create an abstraction I always worry about how it might fail. There's a hopefully-clear error when you read past end of a file.
This commit is contained in:
parent
2d6b88204b
commit
52ae23784b
62
sieve.tlv
62
sieve.tlv
|
@ -265,18 +265,9 @@
|
|||
>
|
||||
>You can also override the default big picture screen entirely by creating a buffer called 'doc:main'.
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 21:49:00 2022
|
||||
>Sat Feb 26 21:50:11 2022
|
||||
main:
|
||||
>function main()
|
||||
> task.spawn(main_task)
|
||||
> task.scheduler()
|
||||
> print('out of scheduler')
|
||||
> Window:getch()
|
||||
>end
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 21:50:11 2022
|
||||
main_task:
|
||||
>function main_task()
|
||||
> local c = task.Channel:new()
|
||||
> task.spawn(counter, c)
|
||||
> for i=1,10 do
|
||||
|
@ -308,8 +299,8 @@
|
|||
>end
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 21:55:46 2022
|
||||
main_task:
|
||||
>function main_task()
|
||||
main:
|
||||
>function main()
|
||||
> local primes = task.Channel:new()
|
||||
> task.spawn(sieve, primes)
|
||||
> for i=1,10 do
|
||||
|
@ -346,29 +337,17 @@
|
|||
>end
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 22:09:47 2022
|
||||
main_task:
|
||||
>function main_task(window)
|
||||
> local primes = task.Channel:new()
|
||||
> task.spawn(sieve, primes)
|
||||
> while true do
|
||||
> window:addstr(primes:recv())
|
||||
> window:addstr(' ')
|
||||
> window:refresh()
|
||||
> end
|
||||
>end
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 22:08:52 2022
|
||||
__teliva_note:
|
||||
>infinite primes
|
||||
main:
|
||||
>function main()
|
||||
> Window:nodelay(true)
|
||||
> Window:clear()
|
||||
> task.spawn(main_task, Window)
|
||||
> task.scheduler()
|
||||
> print('key pressed; done')
|
||||
> Window:nodelay(false)
|
||||
> Window:getch()
|
||||
> local primes = task.Channel:new()
|
||||
> task.spawn(sieve, primes)
|
||||
> while true do
|
||||
> Window:addstr(primes:recv())
|
||||
> Window:addstr(' ')
|
||||
> Window:refresh()
|
||||
> end
|
||||
>end
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 22:09:47 2022
|
||||
|
@ -376,21 +355,26 @@
|
|||
>clear screen when it fills up; pause on keypress
|
||||
>
|
||||
>In Teliva getch() implicitly refreshes the screen.
|
||||
main_task:
|
||||
>function main_task(window)
|
||||
main:
|
||||
>function main()
|
||||
> Window:nodelay(true)
|
||||
> Window:clear()
|
||||
> local primes = task.Channel:new()
|
||||
> task.spawn(sieve, primes)
|
||||
> local h, w = window:getmaxyx()
|
||||
> local h, w = Window:getmaxyx()
|
||||
> while true do
|
||||
> window:addstr(primes:recv())
|
||||
> window:addstr(' ')
|
||||
> local c = window:getch()
|
||||
> Window:addstr(primes:recv())
|
||||
> Window:addstr(' ')
|
||||
> local c = Window:getch()
|
||||
> if c then break end -- key pressed
|
||||
> local y, x = window:getyx()
|
||||
> local y, x = Window:getyx()
|
||||
> if y > h-1 then
|
||||
> window:clear()
|
||||
> Window:clear()
|
||||
> end
|
||||
> end
|
||||
> print('key pressed; done')
|
||||
> Window:nodelay(false)
|
||||
> Window:getch()
|
||||
>end
|
||||
- __teliva_timestamp:
|
||||
>Sat Feb 26 22:27:25 2022
|
||||
|
|
11
src/task.lua
11
src/task.lua
|
@ -380,6 +380,17 @@ _M.RECV = RECV
|
|||
_M.SEND = SEND
|
||||
_M.NOP = NOP
|
||||
|
||||
-- Specific to Teliva
|
||||
function spawn_main()
|
||||
task.spawn(main)
|
||||
task.scheduler()
|
||||
assert(false, "Teliva's scheduler ran out of work; this shouldn't happen.\n"..
|
||||
"Either a channel is blocked forever or you're reading past\n"..
|
||||
"the end of a file (after recv() returned nil).\n")
|
||||
curses.nodelay(true)
|
||||
curses.getch()
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
----------------------------------------------------------------------------
|
||||
-- Tests
|
||||
|
|
|
@ -1794,7 +1794,7 @@ int handle_image(lua_State* L, char** argv, int n) {
|
|||
/* initialize permissions */
|
||||
load_permissions_from_user_configuration(L);
|
||||
/* call main() */
|
||||
lua_getglobal(L, "main");
|
||||
lua_getglobal(L, "spawn_main");
|
||||
status = docall(L, 0, 1);
|
||||
if (status != 0) return report_in_developer_mode(L, status);
|
||||
return 0;
|
||||
|
|
48
template.tlv
48
template.tlv
|
@ -385,3 +385,51 @@
|
|||
> '123 ',
|
||||
> 'test_check_screen')
|
||||
>end
|
||||
- __teliva_timestamp: original
|
||||
start_reading:
|
||||
>-- primitive for reading files from a file system (or, later, network)
|
||||
>-- returns a channel or nil on error
|
||||
>-- read lines from the channel using :recv()
|
||||
>-- recv() on the channel will indicate end of file.
|
||||
>function start_reading(fs, filename)
|
||||
> local result = task.Channel:new()
|
||||
> local infile = io.open(filename)
|
||||
> if infile == nil then return nil end
|
||||
> task.spawn(reading_task, infile, result)
|
||||
> return result
|
||||
>end
|
||||
>
|
||||
>function reading_task(infile, chanout)
|
||||
> for line in infile:lines() do
|
||||
> chanout:send(line)
|
||||
> end
|
||||
> chanout:send(nil) -- eof
|
||||
>end
|
||||
- __teliva_timestamp: original
|
||||
start_writing:
|
||||
>-- primitive for writing files to a file system (or, later, network)
|
||||
>-- returns a channel or nil on error
|
||||
>-- write to the channel using :send()
|
||||
>-- indicate you're done writing by calling :close()
|
||||
>-- file will not be externally visible until :close()
|
||||
>function start_writing(fs, filename)
|
||||
> local result = task.Channel:new()
|
||||
> local initial_filename = os.tmpname()
|
||||
> local outfile = io.open(initial_filename, 'w')
|
||||
> if outfile == nil then return nil end
|
||||
> result.close = function()
|
||||
> result:send(nil) -- end of file
|
||||
> outfile:close()
|
||||
> os.rename(initial_filename, filename)
|
||||
> end
|
||||
> task.spawn(writing_task, outfile, result)
|
||||
> return result
|
||||
>end
|
||||
>
|
||||
>function writing_task(outfile, chanin)
|
||||
> while true do
|
||||
> local line = chanin:recv()
|
||||
> if line == nil then break end -- end of file
|
||||
> outfile:write(line)
|
||||
> end
|
||||
>end
|
||||
|
|
Loading…
Reference in New Issue