# Mu synchronizes between routines using channels rather than locks, like # Erlang and Go. # # The key property of channels: Writing to a full channel or reading from an # empty one will put the current routine in 'waiting' state until the # operation can be completed. # # Beware of addresses passed into channels. They can cause race conditions. scenario channel [ run [ local-scope source:&:source:num, sink:&:sink:num <- new-channel 3/capacity sink <- write sink, 34 10:num/raw, 11:bool/raw, source <- read source ] memory-should-contain [ 10 <- 34 11 <- 0 # read was successful ] ] container channel:_elem [ lock:bool # inefficient but simple: serialize all reads as well as writes first-full:num # for write first-free:num # for read # A circular buffer contains values from index first-full up to (but not # including) index first-free. The reader always modifies it at first-full, # while the writer always modifies it at first-free. data:&:@:_elem ] # Since channels have two ends, and since it's an error to use either end from # multiple routines, let's distinguish the ends. container source:_elem [ chan:&:channel:_elem ] container sink:_elem [ chan:&:channel:_elem ] def new-channel capacity:num -> in:&:source:_elem, out:&:sink:_elem [ local-scope load-inputs result:&:channel:_elem <- new {(channel _elem): type} *result <- put *result, first-full:offset, 0 *result <- put *result, first-free:offset, 0 capacity <- add capacity, 1 # unused slot for 'full?' below data:&:@:_elem <- new _elem:type, capacity *result <- put *result, data:offset, data in <- new {(source _elem): type} *in <- put *in, chan:offset, result out <- new {(sink _elem): type} *out <- put *out, chan:offset, result ] # write a value to a channel def write out:&:sink:_elem, val:_elem -> out:&:sink:_elem [ local-scope load-inputs assert out, [write to null channel] chan:&:channel:_elem <- get *out, chan:offset # block until lock is acquired AND queue has room lock:location <- get-location *chan, lock:offset #? $print [write], 10/newline { #? $print [trying to acquire lock for writing], 10/newline wait-for-reset-then-set lock #? $print [lock acquired for writing], 10/newline full?:bool <- channel-full? chan break-unless full? #? $print [but channel is full; relinquishing lock], 10/newline # channel is full; relinquish lock and give a reader the opportunity to # create room on it reset lock current-routine-is-blocked switch # avoid spinlocking loop } current-routine-is-unblocked #? $print [performing write], 10/newline # store a deep copy of val circular-buffer:&:@:_elem <- get *chan, data:offset free:num <- get *chan, first-free:offset *circular-buffer <- put-index *circular-buffer, free, val # mark its slot as filled free <- add free, 1 { # wrap free around to 0 if necessary len:num <- length *circular-buffer at-end?:bool <- greater-or-equal free, len break-unless at-end? free <- copy 0 } # write back *chan <- put *chan, first-free:offset, free #? $print [relinquishing lock after writing], 10/newline reset lock ] # read a value from a channel def read in:&:source:_elem -> result:_elem, eof?:bool, in:&:source:_elem [ local-scope load-inputs assert in, [read on null channel] eof? <- copy false # default result chan:&:channel:_elem <- get *in, chan:offset # block until lock is acquired AND queue has data lock:location <- get-location *chan, lock:offset #? $print [read], 10/newline { #? $print [trying to acquire lock for reading], 10/newline wait-for-reset-then-set lock #? $print [lock acquired for reading], 10/newline empty?:bool <- channel-empty? chan break-unless empty? #? $print [but channel is empty; relinquishing lock], 10/newline # channel is empty; relinquish lock and give a writer the opportunity to # add to it reset lock current-routine-is-blocked switch # avoid spinlocking loop } current-routine-is-unblocked # pull result off full:num <- get *chan, first-full:offset circular-buffer:&:@:_elem <- get *chan, data:offset result <- index *circular-buffer, full # clear the slot empty:&:_elem <- new _elem:type *circular-buffer <- put-index *circular-buffer, full, *empty # mark its slot as empty full <- add full, 1 { # wrap full around to 0 if necessary len:num <- length *circular-buffer at-end?:bool <- greater-or-equal full, len break-unless at-end? full <- copy 0 } # write back *chan <- put *chan, first-full:offset, full #? $print [relinquishing lock after reading], 10/newline reset lock ] # todo: create a notion of iterator and iterable so we can read/write whole # aggregates (arrays, lists, ..) of _elems at once. scenario channel-initialization [ run [ local-scope source:&:source:num <- new-channel 3/capacity chan:&:channel:num <- get *source, chan:offset 10:num/raw <- get *chan, first-full:offset 11:num/raw <- get *chan, first-free:offset ] memory-should-contain [ 10 <- 0 # first-full 11 <- 0 # first-free ] ] scenario channel-write-increments-free [ local-scope _, sink:&:sink:num <- new-channel 3/capacity run [ sink <- write sink, 34 chan:&:channel:num <- get *sink, chan:offset 10:num/raw <- get *chan, first-full:offset 11:num/raw <- get *chan, first-free:offset ] memory-should-contain [ 10 <- 0 # first-full 11 <- 1 # first-free ] ] scenario channel-read-increments-full [ local-scope source:&:source:num, sink:&:sink:num <- new-channel 3/capacity sink <- write sink, 34 run [ _, _, source <- read source chan:&:channel:num <- get *source, chan:offset 10:num/raw <- get *chan, first-full:offset 11:num/raw <- get *chan, first-free:offset ] memory-should-contain [ 10 <- 1 # first-full 11 <- 1 # first-free ] ] scenario channel-wrap [ local-scope # channel with just 1 slot source:&:source:num, sink:&:sink:num <- new-channel 1/capacity chan:&:channel:num <- get *source, chan:offset # write and read a value sink <- write sink, 34 _, _, source <- read source run [ # first-free will now be 1 10:num/raw <- get *chan, first-free:offset 11:num/raw <- get *chan, first-free:offset # write second value, verify that first-free wraps sink <- write sink, 34 20:num/raw <- get *chan, first-free:offset # read second value, verify that first-full wraps _, _, source <- read source 30:num/raw <- get *chan, first-full:offset ] memory-should-contain [ 10 <- 1 # first-free after first write 11 <- 1 # first-full after first read 20 <- 0 # first-free after second write, wrapped 30 <- 0 # first-full after second read, wrapped ] ] scenario channel-new-empty-not-full [ run [ local-scope source:&:source:num <- new-channel 3/capacity chan:&:channel:num <- get *source, chan:offset 10:bool/raw <- channel-empty? chan 11:bool/raw <- channel-full? chan ] memory-should-contain [ 10 <- 1 # empty? 11 <- 0 # full? ] ] scenario channel-write-not-empty [ local-scope source:&:source:num, sink:&:sink:num <- new-channel 3/capacity chan:&:channel:num <- get *source, chan:offset run [ sink <- write sink, 34 10:bool/raw <- channel-empty? chan 11:bool/raw <- channel-full? chan ] memory-should-contain [ 10 <- 0 # empty? 11 <- 0 # full? ] ] scenario channel-write-full [ local-scope source:&:source:num, sink:&:sink:num <- new-channel 1/capacity chan:&:channel:num <- get *source, chan:offset run [ sink <- write sink, 34 10:bool/raw <- channel-empty? chan 11:bool/raw <- channel-full? chan ] memory-should-contain [ 10 <- 0 # empty? 11 <- 1 # full? ] ] scenario channel-read-not-full [ local-scope source:&:source:num, sink:&:sink:num <- new-channel 1/capacity chan:&:channel:num <- get *source, chan:offset sink <- write sink, 34 run [ _, _, source <- read source 10:bool/raw <- channel-empty? chan 11:bool/raw <- channel-full? chan ] memory-should-contain [ 10 <- 1 # empty? 11 <- 0 # full? ] ] scenario channel-clear [ local-scope # create a channel with a few items source:&:source:num, sink:&:sink:num <- new-channel 3/capacity chan:&:channel:num <- get *sink, chan:offset write sink, 30 write sink, 31 write sink, 32 run [ clear source 10:bool/raw <- channel-empty? chan ] memory-should-contain [ 10 <- 1 # after the call to 'clear', the channel should be empty ] ] def clea
# This file has been modified for lynx (see README.tables)

#The MIME name of this charset.
Mcp737

#Name as a Display Charset (used on Options screen)
OGreek (cp737)

#Codepage number
C737

#
#    Name:     cp737_DOSGreek to Unicode table
#    Unicode version: 2.0
#    Table version: 2.00
#    Table format:  Format A
#    Date:          04/24/96
#    Authors:       Lori Brownell <loribr@microsoft.com>
#                   K.D. Chang    <a-kchang@microsoft.com>
#    General notes: none
#
#    Format: Three tab-separated columns
#        Column #1 is the cp737_DOSGreek code (in hex)
#        Column #2 is the Unicode (in hex as 0xXXXX)
#        Column #3 is the Unicode name (follows a comment sign, '#')
#
#    The entries are in cp737_DOSGreek order
#
##############