Patch title: Release 94 bulk changes
Abstract:
File: /pliant/language/stream/loopback.pli
Key:
    Removed line
    Added line
module "ring.pli"

if os_api="linux"
  module "/pliant/linux/schedule/futex.pli"
constant use_sem3 false # os_api="linux" and os_has_futex
if use_sem3
  module "/pliant/language/schedule/sem3.pli"
  console "Using kernel futexes" eol

type LoopbackBuffer
  field Address buffer ; field Int size
  field Int read_pos write_pos
  if use_sem3
    field Sem3 read_sem write_sem
  else
    field Sem read_sem write_sem
  field CBool eof
  field Sem sem

function build b
  arg_w LoopbackBuffer b
  b size := 2^12
  b buffer := memory_allocate b:size addressof:b
  b read_pos := 0 ; b write_pos := 0
  b eof := false
  b:read_sem request
  
function destroy b
  arg_w LoopbackBuffer b
  memory_free b:buffer
  b:read_sem nowait_request
  b:read_sem release
  b:write_sem nowait_request
  b:write_sem release

gvar Dictionary server_read_buffers
gvar Dictionary server_write_buffers
gvar Dictionary client_read_buffers
gvar Dictionary client_write_buffers
gvar Sem loopback_sem


type LoopbackStreamDriver
  field Link:LoopbackBuffer rd
  field Link:LoopbackBuffer wr
StreamDriver maybe LoopbackStreamDriver

method drv read buf mini maxi -> red
  arg_rw LoopbackStreamDriver drv ; arg Address buf ; arg Int mini maxi red
  var Pointer:LoopbackBuffer b :> drv rd
  red := 0
  while red<mini
    b:read_sem request
    b:sem request
    var Int step := b:write_pos-b:read_pos
    step := min step b:size-b:read_pos
    step := min step maxi-red
    if step=0
      check b:eof
      b:read_sem release
      b:sem release
      return
    memory_copy (b:buffer translate Byte b:read_pos) (buf translate Byte red) step
    red += step
    # if b:write_pos=b:read_pos+b:size
    #   b:write_sem release
    var CBool release := b:write_pos=b:read_pos+b:size
    b read_pos += step
    if b:read_pos>=b:size
      b read_pos -= b size
      b write_pos -= b size
    if b:read_pos<>b:write_pos or b:eof
      b:read_sem release
    b:sem release
    if release
      b:write_sem release

method drv write buf mini maxi -> written
  arg_rw LoopbackStreamDriver drv ; arg Address buf ; arg Int mini maxi written
  var Pointer:LoopbackBuffer b :> drv wr
  written := 0
  while written<mini and not b:eof
    b:write_sem request
    b:sem request
    var Int step := b:read_pos+b:size-b:write_pos 
    step := min step (shunt b:write_pos<b:size b:size 2*b:size)-b:write_pos
    step := min step maxi-written
    if step=0
      check b:eof
      b:write_sem release
      b:sem release
      return
    memory_copy (buf translate Byte written) (b:buffer translate Byte b:write_pos%b:size) step
    written += step
    # if b:read_pos=b:write_pos
    #   b:read_sem release
    var CBool release := b:read_pos=b:write_pos
    b write_pos += step
    if b:write_pos<>b:read_pos+b:size or b:eof
      b:write_sem release
    b:sem release
    if release
      b:read_sem release

method drv flush level -> status
  arg_rw LoopbackStreamDriver drv ; arg Int level ; arg Status status
  status := success

method drv close -> status
  arg_rw LoopbackStreamDriver drv ; arg ExtendedStatus status
  drv:rd:sem request
  drv:rd eof := true
  drv:rd:sem release
  drv:rd:write_sem nowait_request
  drv:rd:write_sem release
  drv:wr:sem request
  drv:wr eof := true
  drv:wr:sem release
  drv:wr:read_sem nowait_request
  drv:wr:read_sem release
  status := success

method drv query command stream answer -> status
  arg_rw LoopbackStreamDriver drv ; arg Str command ; arg_rw Stream stream ; arg_w Str answer ; arg ExtendedStatus status
  if command="remote_ip_address"
    answer := "loopback"
    status := success
  else
    status := failure "unknown command"

method drv configure command stream -> status
  arg_rw LoopbackStreamDriver drv ; arg Str command ; arg_rw Stream stream ; arg ExtendedStatus status
  if command="shutdown"
    drv:rd:sem request
    drv:rd eof := true
    drv:rd:sem release
    drv:rd:write_sem nowait_request
    drv:rd:write_sem release
    drv:wr:sem request
    drv:wr eof := true
    drv:wr:sem release
    drv:wr:read_sem nowait_request
    drv:wr:read_sem release
    status := success
  eif (command parse "buffer_size" (var Int size))
    drv:rd size := size
    drv:rd buffer := memory_resize drv:rd:buffer size (addressof drv:rd)
    drv:wr size := size
    drv:wr buffer := memory_resize drv:wr:buffer size (addressof drv:wr)
  else
    status := failure


type LoopbackFileSystem
  void
FileSystem maybe LoopbackFileSystem

method fs open name options flags stream support -> status
  arg_rw LoopbackFileSystem fs ; arg Str name options ; arg Int flags ; arg_rw Stream stream support ; arg ExtendedStatus status
  if (name eparse "/server/" any:(var Str id))
    var Link:LoopbackStreamDriver drv :> new LoopbackStreamDriver
    loopback_sem request
    var Pointer:Arrow c :> server_read_buffers first id
    if c<>null
      drv rd :> (server_read_buffers first id) map LoopbackBuffer
      server_read_buffers remove id (server_read_buffers first id)
      drv wr :> (server_write_buffers first id) map LoopbackBuffer
      server_write_buffers remove id (server_write_buffers first id)
      loopback_sem release
      drv:wr:sem release
    else
      drv rd :> new LoopbackBuffer
      var Int size := options option "buffer_size" Int
      if size<>undefined
        drv:rd size := size
        drv:rd buffer := memory_resize drv:rd:buffer size (addressof drv:rd)
      client_write_buffers insert id true (addressof drv:rd)
      drv wr :> new LoopbackBuffer
      if size<>undefined
        drv:wr size := size
        drv:wr buffer := memory_resize drv:wr:buffer size (addressof drv:wr)
      client_read_buffers insert id true (addressof drv:wr)
      drv:rd:sem request
      loopback_sem release
      drv:rd:sem request ; drv:rd:sem release
    stream stream_driver :> drv
    status := success
  eif (name eparse "/client/" any:(var Str id))
    var Link:LoopbackStreamDriver drv :> new LoopbackStreamDriver
    loopback_sem request
    var Pointer:Arrow c :> client_read_buffers first id
    if c<>null
      drv rd :> (client_read_buffers first id) map LoopbackBuffer
      client_read_buffers remove id (client_read_buffers first id)
      drv wr :> (client_write_buffers first id) map LoopbackBuffer
      client_write_buffers remove id (client_write_buffers first id)
      loopback_sem release
      drv:wr:sem release
    else
      drv rd :> new LoopbackBuffer
      var Int size := options option "buffer_size" Int
      if size<>undefined
        drv:rd size := size
        drv:rd buffer := memory_resize drv:rd:buffer size (addressof drv:rd)
      server_write_buffers insert id true (addressof drv:rd)
      drv wr :> new LoopbackBuffer
      if size<>undefined
        drv:wr size := size
        drv:wr buffer := memory_resize drv:wr:buffer size (addressof drv:wr)
      server_read_buffers insert id true (addressof drv:wr)
      drv:rd:sem request
      loopback_sem release
      drv:rd:sem request ; drv:rd:sem release
    stream stream_driver :> drv
    status := success
  else
    status := failure
  
gvar LoopbackFileSystem loopback_file_system
pliant_multi_file_system mount "loopback:" "" loopback_file_system