Forums before death by AOL, social media and spammers... "We can't have nice things"
|    sci.logic    |    Logic -- math, philosophy & computationa    |    262,912 messages    |
[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]
|    Message 261,462 of 262,912    |
|    Ross Finlayson to All    |
|    Re: Meta: a usenet server just for sci.m    |
|    29 Nov 25 03:54:06    |
   
   [continued from previous message]   
      
   Process A: read() traps to kernel, blocks, scheduler switches to another   
   process   
   Process B: read() executes as re-routine, yields cooperatively,   
   scheduler runs another re-routine on same thread   
      
   The scheduler tracks: "Is this process re-routine-aware or traditional?"   
   and dispatches accordingly.   
   Message-Passing and Handles:   
   In a system with heavy IPC (like microkernels), the handles are the M-ICCs:   
      
   // Traditional message passing:   
   send(handle, msg);   
   recv(handle, &msg); // Blocks until message arrives   
      
   // Re-routine message passing:   
   RR_ENDER send_rr(MICC handle, Message msg) {   
   MEMO_CHECK(STEP_SEND) {   
   submit_ipc(handle, msg, rr_ctx.current_micc);   
   MEMO_INCOMPLETE();   
   }   
   return MEMO_OK;   
   }   
      
   RR_ENDER recv_rr(MICC handle, Message* msg) {   
   MEMO_CHECK(STEP_RECV) {   
   register_ipc_waiter(handle, rr_ctx.current_micc);   
   MEMO_INCOMPLETE();   
   }   
   *msg = memo_get(rr_ctx.current_memo, STEP_RECV);   
   return MEMO_OK;   
   }   
      
      
   The adapter layer bridges between traditional blocking handles and   
   M-ICC-based re-routine handles.   
   Higher-Level Languages: Java Example:   
   In Java, null becomes the MEMO_INCOMPLETE signal:   
      
   // Re-routine-aware Java method:   
   @ReRoutine   
   public Response httpGet(URL url) {   
   Socket s = tcpConnect(url.getHost(), 80); // May return null   
   if (s == null) return null; // Propagate MEMO_INCOMPLETE   
      
   sendRequest(s, "GET /");   
   Response r = recvResponse(s); // May return null   
   if (r == null) return null;   
      
   return r;   
   }   
      
   // Runtime wraps this to handle nulls:   
   public Response httpGet_wrapped(URL url) {   
   try {   
   Response r = httpGet(url);   
   if (r == null) throw new MemoIncompleteException();   
   return r;   
   } catch (MemoIncompleteException e) {   
   // Resubmit to executor   
   resubmit(this::httpGet, url);   
   return PENDING_SENTINEL;   
   }   
   }   
      
   The Java runtime treats null returns from @ReRoutine methods as   
   cooperative yield points. The NullPointerException becomes a structured   
   control flow mechanism rather than an error.   
   This is controversial but elegant - nullability is always a hazard in   
   Java, but here it's opt-in (only in @ReRoutine methods) and well-defined   
   (always means "not ready yet").   
   Function Overloading: Re-Routine Agnostic Libraries:   
   In C++ with TTASL extensions:   
      
   // Synchronous version (traditional):   
   ssize_t read(int fd, void* buf, size_t count);   
      
   // Re-routine version (overloaded by convention):   
   ssize_t read(int fd, void* buf, size_t count, rr_tag_t);   
      
   // Usage:   
   void legacy_code() {   
   char buf[512];   
   read(fd, buf, 512); // Calls blocking version   
   }   
      
   RR_MAKER void async_code() {   
   char buf[512];   
   read(fd, buf, 512, RR_TAG); // Calls re-routine version   
   }   
      
      
   The rr_tag_t is a zero-size type (empty struct) that only affects   
   overload resolution. The compiler recognizes RR_TAG and generates   
   re-routine calling convention code.   
   Alternatively, Koenig lookup (ADL) in C++:   
      
   namespace rr {   
   struct Context { /* thread-local state */ };   
      
   ssize_t read(int fd, void* buf, size_t count, Context& ctx);   
   }   
      
   RR_MAKER void async_code() {   
   char buf[512];   
   read(fd, buf, 512, rr::current_context); // ADL finds rr::read   
   }   
      
   The presence of rr::Context argument triggers the re-routine version.   
   TTASL as Vanilla Assembler with Convention:   
   TTASL is not a new language - it's a disciplined subset of assembler   
   with compiler-recognized patterns:   
      
   Traditional assembler (no re-routine):   
   tcp_connect:   
   push rbp   
   mov rbp, rsp   
   ... do work ...   
   pop rbp   
   ret   
      
   TTASL re-routine assembler (with convention):   
   tcp_connect_rr:   
   Compiler recognizes .rr_maker directive   
   .rr_maker FSM_TCP_CONNECT   
      
   Standard prologue   
   push rbp   
   mov rbp, rsp   
      
   Memo check (compiler-generated):   
   mov rax, [rip + rr_ctx@GOTPCREL]   
   mov rdi, [rax + OFFSET_MEMO]   
   mov esi, STEP_SOCKET   
   call memo_has   
   test al, al   
   jnz .step_socket_filled   
      
   First time - do work   
   call create_socket   
   mov rdi, [rip + rr_ctx@GOTPCREL]   
   mov rdi, [rdi + OFFSET_MEMO]   
   mov esi, STEP_SOCKET   
   mov rdx, rax   
   call memo_set   
   jmp .memo_incomplete   
      
   .step_socket_filled:   
   Memo filled - retrieve value   
   mov rdi, [rip + rr_ctx@GOTPCREL]   
   mov rdi, [rdi + OFFSET_MEMO]   
   mov esi, STEP_SOCKET   
   call memo_get   
   rax now contains socket   
      
   ... continue with next step ...   
      
   .memo_incomplete:   
   Compiler-recognized exit path   
   mov rdi, [rip + rr_ctx@GOTPCREL]   
   lea rsi, [rip + .resume_point]   
   call longjmp_reroutine   
      
   .resume_point:   
   Will never execute in this invocation   
      
   pop rbp   
   ret   
      
   The directives (.rr_maker, .rr_passer, .rr_ender) tell the TTASL compiler:   
      
   Where to insert memo checks   
   How to compute offsets for submemos   
   Which calling convention to use   
      
   The templates are just macro expansion with offset computation:   
      
   Template definition:   
   .macro MEMO_CHECK step_label, step_id   
   mov rax, [rip + rr_ctx@GOTPCREL]   
   mov rdi, [rax + OFFSET_MEMO]   
   mov esi, \step_id   
   call memo_has   
   test al, al   
   jnz \step_label\()_filled   
   .endmacro   
      
   Usage:   
   MEMO_CHECK .step_socket, STEP_SOCKET   
   Work to do if not filled   
   call create_socket   
   MEMO_SET STEP_SOCKET, rax   
   MEMO_INCOMPLETE   
   .step_socket_filled:   
   MEMO_GET STEP_SOCKET   
   rax now contains result   
      
      
   This is standard assembler macro facility - TTASL just defines a   
   standard set of macros for the re-routine convention.   
   C/C++ Interoperation:   
   The compiler recognizes the calling convention by function attributes:   
      
   // C with re-routine extension:   
   __attribute__((reroutine_maker))   
   Response http_get(URL url) {   
   // Compiler generates:   
   // 1. Prologue: set up rr_ctx   
   // 2. setjmp at entry   
   // 3. MEMO_CHECK before each call   
   // 4. longjmp on MEMO_INCOMPLETE   
   Socket s = tcp_connect(url.host, 80);   
   return recv_response(s);   
   }   
      
   // Assembly produced:   
   http_get:   
   .rr_maker FSM_HTTP_GET   
   ... compiler-generated re-routine machinery ...   
      
   For mixed codebases (legacy + re-routine), the linker resolves symbols   
   normally:   
      
   // Legacy library (no re-routine):   
   void legacy_process(int fd);   
      
   // New re-routine code calls legacy:   
   RR_MAKER void new_code(int fd) {   
   legacy_process(fd); // Compiler inserts blocking adapter   
   }   
      
   The compiler detects that legacy_process lacks the re-routine attribute   
   and inserts an adapter shim:   
      
   // Compiler-generated adapter:   
   void legacy_process_adapter(int fd) {   
   MEMO_CHECK(STEP_LEGACY) {   
   // Run legacy code in separate thread/context   
   submit_blocking_call(legacy_process, fd, rr_ctx.current_micc);   
   MEMO_INCOMPLETE();   
   }   
   return; // Legacy function returned, memo filled by worker thread   
   }   
   ```   
      
   This allows **gradual migration** - old code works unchanged, new code   
   gets re-routine benefits.   
      
   ---   
      
   **Why This Achieves "Re-Routine Agnostic" Libraries:**   
      
   The standard library becomes a **multi-target compilation**:   
   ```   
   libc.a (blocking version) → traditional processes   
   libc_rr.a (re-routine version) → re-routine-aware processes   
   libc_dual.a (both) → runtime detection   
      
   The linker chooses the appropriate version based on the target process   
   type. A single source base compiles to multiple ABIs.   
   The Levity Earned:   
   You're right that this is deeply satisfying for anyone who's fought:   
      
   Callback hell (JavaScript promises, Rust futures)   
   Explicit async/await (C# async methods, Python asyncio)   
   Monad transformers (Haskell IO)   
   Green threads (Go goroutines with hidden scheduler)   
      
   Re-routines achieve implicit async - you write synchronous code, the   
   compiler inserts yield points, the runtime provides fair scheduling.   
   It's the Goldilocks solution: more structured than callbacks, less   
   invasive than async/await, more efficient than threads.   
   Final Architectural Insight:   
   The re-routine model unifies three concepts that are usually separate:   
      
   Concurrency primitive (like threads or coroutines)   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|
[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]
(c) 1994, bbs@darkrealms.ca