On 08/05/2014, at 11:55 AM, srean wrote:

> Woohoo! this does answer my question to John "Is victory yours ?"

> However Mark reports his build still doesn't work.

Whilst Mark's build doesn't work, there's still a problem.
It may be in his clang (he's running clang on Linux).

however my build on OSX (clang) and the build on Ubuntu(gcc)
both seem to work, both web servers seem to run ok, and I can
run the tests with FLX_MIN_MEM=0 and FLX_FINALISE=1 which puts
maximum stress on the GC, on both systems.

It's possible Mark has a later clang than me, and it may need
some extra flags to turn off overly eager optimisations not available
on my build. Hard to know.

At some stage I should make a separate directory on the server,
and build the latest clang and use it. However I'm loathe to do that
because of the confusion about which compiler is being used.
I already have a weird setup on my Mac with both gcc 4.2 and
clang 3.3(svn) installed, and I don't know which C++ library the
clang is using. Whilst clang++ supports --std=c++11 the library
it uses definitely is NOT C++11 compliant. Its hard to tell if it
is using libc++ (the clang library) or libstdc++ (the gcc library).

In the mean time there are still some issues. One is that a
direct call to the GC via collect() seems to bypass a mutex lock.
I think this is deliberate (it says so) but I will have to analyse
what's going on. Both an allocation (operator new) and a direct
call can trigger a collection. Allocations are mutex protected
so they're serialised. If a GC is needed a semaphore is set up
and the mutex is release so other threads can attempt an allocation,
only to find they're requires to stop until after the GC is done.
A direct call to collect should work the same way, in fact the 
requested collection should be skipped if there's a world_stop
in progress because some other thread is waiting to do a collection
anyhow.

<rant>

The structure of the GC is fairly sophisticated. I should write an article
on this since the vast majority of C++ lessons are utterly wrong.
In fact Bjarne is reported to believe "protected" access was his
worst mistake in C++ and he's wrong too. Its not only not a mistake
it isn't adequate. So you know what i'm ranting about: virtual
functions in C++ should be "absolute private" meaning not only
private, but also all overrides should be *completely* hidden
(even from the class doing the overriding).

There's one, and ONLY one, way to do virtual dispatch right
and everyone gets it wrong. The GC shows how to do it correctly.
The strict design rules applied are essential, but its confusing
because there are always THREE variants of a function:

        public: only in the abstract base, to be called ONLY by the public
                must NOT be called from inside the class
                In the gc, "get_allocation_amount" is an example
                These routine call the private virtual routines.
                NO ONE ELSE is allowed to do that.

        protected: these routine contain the actual code to be used
                inside  the class or derived classes.  They contain the
                algorithms for the state of the current class.
                They cannot be called by the public. For example:
                "impl_get_allocation_amount"

                An implementation in a derived class can call
                an implementation routine in a base.

        private virtual: these are the actual implementation dispatchers.
                They never do any actual work, but they call the non-virtual
                protected implementation routines for that class. 
                These routines must NEVER be called by anyone,
                except the public members in the base class.
                Example "v_get_allocation_amount".


So the way it works is: the public functions call the virtual functions,
which depend on the complete class. These functions then call
the protected functions to do the work. Protected functions can
call private non-virtuals of the current class or protected functions
of the base.

The reason for this structure is all to do with preserving invariants.
Public methods enforce preconditions to the virtuals. These preconditions
don't apply "in the middle of a calculation" so ONLY the public is allowed
to call these routines.

The protected (and non-virtual private helpers) provide implementations
appropriate for a class. A protected function can be called by other
protected functions in the same or a derived class. These routines
do the actual work, and they're all non-virtual.

The reason the GC uses this complex structure is the three level 
structure:

        abstract collector -> concrete collector -> thread safe collector

The concrete collector isn't thread safe. The thread safe collector,
on the other hand, doesn't want to know ANYTHING about how the
collector algorithms work, it just throws in some locks to ensure access
is serialised where required, then dispatches up to the concrete
collector. All this has to work with calls via the abstract base.

So the calls go:

        BASE:       public calls to collect -> 
        THREAD: thread safe virtual ->
        THREAD: thread safe implementation (sets lock) -> 
        UNSAFE: unsafe implementation

Now cross calls in the unsafe implementation do the work, all under the
scope of the lock.

The semantics are guaranteed because:

        (a) the only way to get a lock is via the thread_safe collector
        implementation routine which is only called by the virtual
        override which can only be called by the public interface
        which can only be called by the public

        Therefore, only ANOTHER thread can find the lock locked.
        (assuming we do not call out to another routine that calls the GC
        from outside).

        (b) Only the public can find set the lock

So neither a race, nor a deadlock, is possible.
the type system (and access control) guarantee this PROVIDED
you don't cheat and call a virtual function in any code than the
abstract base wrappers. It helps to always make the virtuals private
but it isn't enough, since in a derived class you're not allowed to
call a virtual either.

So Stroustrup got it wrong. Not only is protected essential,
virtual should imply private, and a virtual override should 
be completely inaccessible.

</rant>

--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
Is your legacy SCM system holding you back? Join Perforce May 7 to find out:
&#149; 3 signs your SCM is hindering your productivity
&#149; Requirements for releasing software faster
&#149; Expert tips and advice for migrating your SCM now
http://p.sf.net/sfu/perforce
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to