Michal Wallace wrote:
> 
> On Thu, 21 Aug 2003, Benjamin Goldberg wrote:
> 
> > I hope you aren't planning on serializing just a single isolated
> > microthread... that wouldn't work well with what I've got in mind due
> > to how much stuff comes along when you serialize a continuation --
> > you'd get almost the whole interpreter serialized.
> >
> > If you want, instead, to serialize interpreter->microthreads,
> > however...
> > well, you'd *still* get almost the whole interpreter serialized, but
> > you're getting more bang for your buck :)
> 
> Well how else are we going to implement squeak? :)

Squeak uses synchronous communication channels, right?  Writing blocks
until there's a reader, and reading blocks until there's a writer.

I just thought of a way of implementing such things for microthreads :)

Something like the following:

   enum {
      enum_state_none = 0,
      enum_state_ready = 1,
      enum_state_readers = 2,
      enum_state_writers = 4,
   };
   pmclass SynchronousChannel {
      void init() {
         SELF->cache.int_val = enum_state_none;
         PMC_data(SELF) = pmc_new(INTERP, enum_class_PerlArray);
         PObj_custom_mark_SET(SELF);
      }
      void mark() {
         pobject_lives(INTERP, PMC_data(SELF));
      }
      void push_pmc(PMC *the_val) {
         if( SELF->cache.int_val & enum_state_ready )
            internal_exception(???, "Illegal state\n");
         SELF->cache.int_val |= enum_state_ready
         VTABLE_push(INTERP, PMC_data(SELF), the_val);
      }
      PMC * shift_pmc() {
         if( !(SELF->cache_int_val & enum_state_ready) )
            internal_exception(???, "Illegal_state");
         SELF->cache_int_val &= ~enum_state_ready
         return VTABLE_pop(INTERP, PMC_data(SELF));
      }
      void* invoke(void* next) {
         PMC * data = (PMC*)PMC_data(SELF);

         PMC * cont;
         switch( SELF->cache.int_val ) {
            case enum_state_ready:
            case enum_state_ready|enum_state_writers:
            case enum_state_ready|enum_state_readers:
            case enum_state_none:
            case enum_state_readers:
               cont = pmc_new(INTERP, enum_class_Continuation);
               VTABLE_set_integer_native(INTERP, cont, (INTVAL)next);
               break;
         }

         switch( SELF->cache.int_val ) {
            /* ready means we're trying to write data */
            case enum_state_ready:
            case enum_state_ready|enum_state_writers:
               /* if there are no readers, suspend ourself */
               SELF->cache.int_val = enum_state_writers;
               VTABLE_push_pmc(INTERP, data, cont);
               break;
            case enum_state_ready|enum_state_readers:
               /* otherwise, stay awake, and wake up */
               /* (and switch to) a reader. */
               VTABLE_push_pmc(INTERP, interpreter->microthreads, cont);
               cont = VTABLE_shift(INTERP, data);
               if( VTABLE_get_integer(INTERP, data) == 1 )
                  /* Remove _readers flag */
                  SELF->cache.int_val = enum_state_ready;
               return VTABLE_invoke(INTERP, cont);
            /* not-ready means we're trying to read data */
            case enum_state_writers:
               /* if data has been written, we stay awake, */
               /* and we wake up the writer who wrote. */
               SELF->cache.int_val = enum_state_ready;
               VTABLE_push_pmc(INTERP, interpreter->microthreads,
                  VTABLE_shift(INTERP, data));
               if( VTABLE_get_integer(INTERP, data) > 1 )
                  SELF->cache.int_val |= enum_state_writers;
               return next;
            case enum_state_none:
               SELF->cache.int_val = enum_state_readers;
               /* fallthrough */
            case enum_state_readers:
               /* If no data has been written, we suspend ourself */
               VTABLE_push_pmc(INTERP, data, cont);
               break;
            case enum_state_readers|enum_state_writers:
            case enum_state_readers|enum_state_writers|enum_state_ready:
               internal_exception(???, "Illegal state");
               break;
            default:
               internal_exception(???, "Illegaller state");
               break;
         }
         if(!VTABLE_get_integer(INTERP, interpreter->microthreads))
            internal_exception(???, "Deadlock!");
         cont = VTABLE_shift(INTERP, interpreter->microthreads);
         return VTABLE_invoke(INTERP, cont, next);
      }
   }

Writing to a channel is done with:
   push $Pchannel, $Pdata
   invoke $Pchannel
Reading from a channel is done with:
   invoke $Pchannel
   shift $Pchannel, $Pdata

We can detect when the whole system is deadlocked, since ->microthreads
becomes empty.

A partial deadlock, though, is undetectable, since there's no way to
know whether or not one of the 'live' threads might happen to write to
one of the channels that the deadlocked threads are reading from, or
might happen to read from a channel that a deadlocked thread is writing
to.

I believe that it would require solving the halting problem to be able
to detect that a partial deadlock will remain locked forever.

If full deadlock does occur, then we can probably find out what went
wrong, by looking through all the syncchannel objects, and inducing the
all of the Continuation pmcs they contain to barf up stacktraces.

-- 
$a=24;split//,240513;s/\B/ => /for@@=qw(ac ab bc ba cb ca
);{push(@b,$a),($a-=6)^=1 for 2..$a/6x--$|;print "[EMAIL PROTECTED]
]\n";((6<=($a-=6))?$a+=$_[$a%6]-$a%6:($a=pop @b))&&redo;}

Reply via email to