[Openmcl-devel] type specifier '(simple-vector n) in defmethod

David McClain dbm at refined-audiometrics.com
Sat Jan 6 06:53:30 PST 2024


I have been using Async Socket I/O in Lispworks, qute successfully for several years now. Even with multiple connections, it requires only a single machine thread to run a dedicated socket handler. LW must be using a Select() down deep inside, but I haven’t looked. There is no problem with idle socket connections. 

The dedicated handler thread hands off incoming packet fragments to one or more of my own waiting user code fragments which run in one of my pool threads. The entire thing runs in my laboratory with 4 or more networked machines, where I have in each machine just one socket handler thread, and a pool of 8 worker threads for myself. None of my code sits waiting for incoming socket data, but responds whenever fresh data arrive.

I have no idea how complex the underlying Async I/O in Lispworks really was to write. But I don’t imagine that it was horrendous. Most likely much less complex than having to deal with the OpenSSL mess.

> On Jan 6, 2024, at 07:00, Nicolas Martyanoff <nicolas at n16f.net> wrote:
> 
> "R. Matthew Emerson" <rme at acm.org> writes:
>> What do you mean by a “userland thread based runtime”? Does that mean
>> that you would prefer cooperatively-scheduled / coroutine style Lisp
>> processes (like old MCL had)?
>> 
>> Dealing with preemptively-scheduled OS threads is a pain, but
>> multicore processors are extremely common, and it seems important for
>> Lisp to be able to make use of them.
> 
> CCL processes, the same was as SBCL threads, are directly mapped to OS
> threads. OS threads are heavy, you cannot just create millions. So to
> deal with concurrent IO operations (my bread and butter), you need
> epoll/kqueue[1] and you end up in callback hell where your entire
> application is a state machine where every IO operation has to be
> non-blocking and you have to be incredibly careful not to block the main
> loop (usually delegating work to a thread pool e.g. for disk
> operations).
> 
> In CL, this really does not map well to streams because streams are
> fundamentally blocking (see for example the semantic of READ-SEQUENCE).
> I have some experimental code for non-blocking streams [2], but it is
> awkward to use and you have to write your parsers very carefully. This
> also leads to highly complex multithreaded code (interaction between IO
> event handling and workers), and no one is smart enough for that.
> 
> The right way, as done in Erlang and more recently Go, is to use green
> (userland) threads mapped to multiple OS threads. Each green thread can
> be very lightweight (an Erlang process starts with less than 2kB of
> heap), start almost instantly and you can spawn millions of them. The
> runtime handles scheduling; usually you yield on IO operations and
> every N operations ("reductions" in Erlang) otherwise.
> 
> This means that you can just spawn one green threads per network
> connection (or per concurrent operation, really), write your code the
> same way as if it was a non-concurrent program and communicate using CSP
> as in Go. In CL, this means one could use streams with the exact same
> semantics while the underlying concurrent runtime would handle IO events
> correctly.
> 
> Useless to say that modifying an existing compiler to switch to this
> model is a huge endeavour, which is why I'm saying that if I were to try
> this, I would write an implementation from scratch.
> 
> 
> [1] Not just to keep memory usage low, but because an idle network
> connection blocks its thread potentially forever. Impossible to expose a
> server with this risk.
> 
> [2] https://github.com/galdor/tungsten/blob/master/tungsten-system/src/tcp-stream.lisp
> 
> -- 
> Nicolas Martyanoff
> https://n16f.net
> nicolas at n16f.net



More information about the Openmcl-devel mailing list