Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 16 Jul 2002 19:59:37 -0700
From:      Terry Lambert <tlambert2@mindspring.com>
To:        Jordan DeLong <fracture@allusion.net>
Cc:        Taavi Talvik <taavi@uninet.ee>, freebsd-chat@FreeBSD.ORG
Subject:   Re: Linker sets portability
Message-ID:  <3D34DD99.11FF8526@mindspring.com>
References:  <20020717014008.Y99892-100000@valu.uninet.ee> <3D34AC52.2D882455@mindspring.com> <20020717005629.GA42607@allusion.net>

next in thread | previous in thread | raw e-mail | index | archive | help
Jordan DeLong wrote:
> > Microsoft DLL's have that ability, derived from OLE, and based on
> > the current COM (Common Object Model) to create arbitrary support
> 
> Component object model actually, I believe.

I don't know; "Common Object Model" is what they called it in
the "ViPER Microsoft ActiveX Preview" back in June of 1996,
when they gave me the CDROM to run on Microsoft Windows NT 4.0
Beta 2.  8-) 8-).


> If you spend a few moments playing with COM, you'd probably realize
> that the COM threading models are so painfully braindead it's not even
> funny (and they're a symptom of the rampant misuse of threading found
> in win32, not a sign of win32 being ahead of unix).

I guess I'll have to turn in my "Microsoft ActiveX Server Design
Team" T-Shirt at the front office?  8-).

Actually, they are very natural, once you understand why they
are the way they are.  Specifically, the reasons behind the
reasons behind marshalling have to do with WIN32.DLL on Windows
3.11.


> Basically if you want to use more than one appartment in the same
> process, you're going to have trouble (and keep in mind this is
> the whole theoretical point of the threading models):
> 
> Because calls into the STA from other appartments are implemented
> by using a hidden "COM Window" and packaging every call into a
> windows message, anytime you enter a modal message pump from the
> main STA another thread can renter the STA's objects -- and there
> is *no* way to effectively lock them out as one would normally do
> in an MTA app because it's the same physical thread, even though
> it is a different "logical" thread (i.e. it would own any critical
> section objects already, etc).  This behavior is entirely unpredictable
> and unavoidable, because you have no control over the internals
> IMessageFilter can't solve the problem, but i'm getting off track)
> of modal message pumps (which exist in several apis).

Actually, we faced this problem at Artisoft, when we implemented
soft updates in 1995, after we ported the Heidemann VFS stacking
framework and the Berkeley FFS to Windows95 (well before soft
updates were supported on any BSD -- Matt Day did most of the
work on the Soft Updates implementation).

The trick is that you can't use a semaphore, you  have to use a
mutual exclusion.  This bit us when the timer thread would fire
for the kernel thread we had running the soft updates update
clock, since it would basically grab any thread context to run
on (like FreeBSD has been proposing to do for interrupt threads).

If you use the real mutual exclusion primitives, rather than
the semaphore calls, it's not a problem.  For this to work,
you have to "OpenDriver" a VXD that implements the exlusion
mechanism using the kernel primitives.  It's really pretty
trivial, if you have the DDK license, which any (at the time)
"MSDN Level 2" or better developer network member had (it was
a matter of paying for the SDK's and DDK's, in other words).


> OTOH if you put stuff into the MTA and try to access other pieces
> in an STA which may call back into it (the mta), win32 will actually
> spawn a new thread for every method call back into the MTA.  So
> that's gross, but it's worse than just gross:  because this is a
> differen't physical thread (but the same "logical" thread, if you
> take my meaning) it can't recurse on mutexes.

You never want recursion on mutexes.  If your code recurses on
mutexes, it's broken, unless you have explicit recursion counting
so that you know it's happening.  With "context stealing", there
is really no way to make provably correct code, if you permit
mutex recursion.  So, actually, I would blame your code, not the
ViPER framework.


> I don't see why the win32 people couldn't have just done their
> component thing without regard to threading, and let individual
> app/component writers deal with it.  It's just trying to solve too
> many (nonexistant) problems with one architecture (for more of this
> see their new COR/CLR/.NET (whatever it's called this week) replacement
> for COM+).

The problems it's solving are real.  The Windows 3.11 system with
the WIN32.DLL and the corresponding VxD in it was incapable if
supporting a "freethreading" model.  This is because the data in
any heap memory was allocated in a 64K medium model chunk, the
address of which was the instance ID for the message pump, when
you were running multiple instances of messages.  The best that
they could do is to create the concept of TLS "Thread Local Storage",
and live with it.

For the programs to run on both Windows 3.11 with the WIN32 API
spackled onto it, and on newer Windows95 and above versions, the
concept of "marshalling" data to get it between one set of TLS
and another was required.  Actually, due to the way VMM32.VxD
was written, you actually don't need to marshal object instances
created *before* a thread is created -- there will be a duplicate
mapping.

In the free-threading case, there was an attempt to make the API
compatible between NT and Windows 95/98.  For that to work, the
marshalling ("CoCreateFreeThreadedMarshaller()") was required,
since the data may be in a different address space, and that's
what enabled DCOM ("COM+" as you've called it).

What Apartment model threading buys you is the ability to
serialize requests into an otherwise unsafe legacy inteface,
using a simple to write wrapper around the Thunk entry points.
Rental model threading permits the use of local globals, so
long as they aren't used simultaneously (in the FreeBSD world,
this would be like implementing the interface "x_r()" via a
set of locking primitives and a call to "x()").  Again, it
permits a certain level of backward compatability, and, if the
code is written correcly, it can attempt to set freethreding,
fail to do so, and continue to run (in a degraded peroformance
mode) successfully.

Other than the different address spaces, which require marshalling
on the local machine, and the registration and service location
mechanisms, which are solvable with protocols like SLPv2, rather
than jamming raw IP addresses into DCOM object server lists,
Microsoft did a pretty incredible job with what they had.

For implicitly serialized protocols, where pipelining is not a
possibility, UNIX could really use the ability to rental or
apartment model the libraries that interfaced to the protocol
engine.  Specifically, there has been a lot of recent bitching
about the resolver library not being asynchornous.  With an
apartment model -- which requires the ability to know when
threads attach and detach a library, so that the library can
create/delete per thread context objects -- it's possible to
export an asynchornous interface, while handling the serial
nature of the current resolver library.  Then code would not
have to change, were the resolver library ever pulled out of
libc, so that it could be easily fixed ("the BSD way" can be
preserved by linking the ELF C library against the ELF resolv
library: one of the base arguments used for the ELF switch in
the first place).

Similarly, all of the "x" and "x_r" versions of routines could
have been resolved via "rental model", without having to introduce
a new API.  For each instance of a thread, there would be a
late-bound allocation of a thread specific "global" context,
and the APIs would "just work" without all the glue and kludgery
that we see today.  The contexts would be freed on "thread
detach", so there would be no memory leak.

Say what you will about them, they thought a number of these
issues out, while UNIX has been sitting on it's academic laurels
and doing nothing.


> [ ... ]
> > So the general answer is "pretty much everyone whose linker is
> > capable of linking programs compliant with the C++ standards,
> > and whose C compiler supports a sufficiently sophisticated ability
> > to escape to inline assebly to generate the data that the linker
> > uses to accomplish C++ specific features, can be made to support
> > linker sets".
> 
> OT: (This isn't important to the point you were making... but then
> again neither was the COM stuff above :P). I don't think a truely
> standards compliant C++ compiler exists...  neither G++ nor MSVC++
> comply to the standard completly..

The point is that people have been trying to for a long time.  It
used to be that there were no shared template instances.  Then,
you had to compile a "magic" version of the template code with a
compiler flag, to get a shared instance.  Then the compilers
started supporting auto-aggregation with duplicate supression,
and all that garbage wasn't necessary any more.

You can assume that linker sets are portable to the platforms
he listed (Linux/FreeBSD/Win32/Solaris), and the "/..." is
the part that needed his iown observations for answering.

Most compilers these days *at least* support that much of the
standard that you can have a template class with a static
instance member variable that is shared among all instances;
and if the compiler supports it, there's usually some way to
abuse it to get a "linker set" equivalent, although whether it
can be achieved inline is always a function of the flexibility
of the C compiler itself (e.g. inline assembly and segment
attribution, to access the linker feature that was intended for
use by C++ for that purpose).

-- Terry

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-chat" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3D34DD99.11FF8526>