On Thu, 05 Oct 2000  23:06:08 +0900, YANAGAWA Kazuhisa wrote:
> In message <877l7o375b.wl / studly.netlab.co.jp>
> shugo / ruby-lang.org writes:
> 
> > > class YASizedQueue < YAQueue
> > (snip)
> > >   def enq(obj)
> > >     @mutex.synchronize do
> > >       while @que.length >= @max
> > >         @full_cond.wait(@mutex)
> > >       end
> > >       super(obj)
> > >     end
> > >   end
> > 
> > Sorry, Mutex is not re-entrant. (So this program cause dead-lock.)
> > Please use Monitor instead of Mutex.
> 
> NOTE: A problem above is a one of similar situations known as
> ``inheritance anomaly'' --- embedded synchronization codes prevent
> ``normal'' inheritance where one can use super class's code without
> knowledge of its implementation.  Even merely adding constraints may
> need to reimplement other methods.

Agreed. Not only do you inherit an interface, you also inherit (a)
state (machine) that defines the interface's behaviour. This violates
the encapsulation principle. To solve this we could refactor Queue and
siblings:

  AbstractQueue  - a generic queue w/o synchronisation stuff
    Queue        - or `SynchronizedQueue', a synced queue
    SizedQueue   - does _not_ inherit from Queue due to ``inheritance
                   anomaly''
    ...

The synced variants are probably best made `final'.
We should analyze this a bit deeper and maybe introduce some patterns.
State, Strategy and Adapter come to mind.

And another thing (which follows from the above):
To acquire a clean, simple and understandable set of synchronisation
primitives it would, IMO, be a Good Thing to implement all of them
_only_ using the Thread.critical primitive (which Thread.exclusive
nicely wraps). At the possible cost of some code duplication,
decoupling is achieved. Easier to test, easier to maintain.

	- Michel