Anyway, could anyone point me to: 1) a more in-depth explanation of this
error code 2) a good reference to help me sort out my marshalling/RPC
problems here, 3) a reference of how other developers have handled the
problem of full-duplex COM-object communication?
Justin Grant
Finnegan O'Malley & Company Inc.
--
Justin Grant
Finnegan O'Malley & Company Inc.
The server sends the event to the client from thread A's message queue, the
client holds a pointer to the server's object which is also in thread A,
and therefore needs thread A's message queue to pre-empt the thread. See
the problem? The client can't communicate with the server because the very
same message queue which is used in the communications is currently being
blocked by the call waiting to return from the client.
The solution? Implement the server's transaction object in a different
thread to that which initiates the event on the client.
If you were a real dude - you'd use Microsoft Transaction Server and save
yourself weeks of work in thread management ;) For more info, go to
http://microsoft.com/transaction.
Hope this helps,
John Wood
Senior Programmer
Royalblue plc
Justin Grant <Jus...@FOMCI.com> wrote in article
<01bbf3e3$8e5a7800$83dfb1cc@justin-wnt>...
The best way is to avoid this altogether. If that is not possible,
simply post yourself a message, and let that message handle the gore of
the same operation. COM is happy now, because posting is an async
operation. Of course, you may have to wait for the completion if any
other segment of your code relies on the result of this operaton.
This in itself is not directly related to threads, but another close
issue is. If your source calls a sink from a thread, and the sink
attempts to interact with the source from within the same thread,
you've set yourself up for a dog-eating-tail. You can ask the message
filter to intimate your of such eventualities (perhaps a default
handler does indeed do that). This puts another RPC complaint:
RPC_SAMETHREAD (surprise!) or something. Check OLE 2 Prog. Guide.
- Lakshmi N.
In message <01bbf3e3$8e5a7800$83dfb1cc@justin-wnt>
"Justin Grant" <Jus...@FOMCI.com> posts:
One question: when I do a CoCreateInstance on a progID to create an
object, what thread "owns" (i.e. uses the message queue of for RPC) the
object that is created? I'm assuming that it's the first thread that called
CoInitialize().
Justin
John Wood <ten...@msn.com> wrote in article
<01bbf462$efa28b00$5d4d95c1@snake>...
> From my experience in using earlier versions of DCOM and using a similar
> setup, I would say that it was caused by the fact that all marshaling
from
> machine to machine and process to process is done via message queues.
>
> The server sends the event to the client from thread A's message queue,
the
> client holds a pointer to the server's object which is also in thread A,
> and therefore needs thread A's message queue to pre-empt the thread. See
> the problem? The client can't communicate with the server because the
very
> same message queue which is used in the communications is currently being
> blocked by the call waiting to return from the client.
>
> The solution? Implement the server's transaction object in a different
> thread to that which initiates the event on the client.
>
> If you were a real dude - you'd use Microsoft Transaction Server and save
> yourself weeks of work in thread management ;) For more info, go to
> http://microsoft.com/transaction.
>
> Hope this helps,
>
> John Wood
> Senior Programmer
> Royalblue plc
>
> Justin Grant <Jus...@FOMCI.com> wrote in article
> <01bbf3e3$8e5a7800$83dfb1cc@justin-wnt>...
> > I'm writing an ActiveX Control / OLE Automation OutProc Server, each
of
> > which talk to the other via Connection Points (Server to Control) and
OLE
> > Automation (Control to Server); the control sends commands to the
server
> > and the server sends "events" back to the control. I'm getting
> exceptions
> > in the control within the RPC DLL's with
RPC_E_CANTCALLOUT_INEXTERNALCALL
> > (0x80010005, "It is illegal to call out while inside message filter")
as
> > the error. I have a pretty good idea what's happening here: my control
> is
> > trying to access the server when another thread on the server is
sending
> an
> > "event" to the control. (Or maybe vice versa.)
> >
> > Anyway, could anyone point me to: 1) a more in-depth explanation of
this
> > error code 2) a good reference to help me sort out my marshalling/RPC
> > problems here, 3) a reference of how other developers have handled the
> > problem of full-duplex COM-object communication?
> >
For this reason you have to have 2 threads for every client - one used for
communication in one direction, and one for the other. In this case, the
threads for the 1000 clients would concurrently wait, but the 1001st
client's thread would make sure it got the event straight away - with no
pause at all.
Now to answer your question....
This depends on whether you are using NT4 or 3.51/95: The difference being
that NT4 uses free-threading model, and the other two use apartment model
threading.
I believe in 3.51 and 95 that you need to execute CoInitialize in every
thread (although I should say apartment) in which you use OLE/COM. In the
free-threaded model you don't *have* to.
What ever thread you CoCreate in, it will be through that thread's message
queue that access to your object will be performed.
Try and stick to free-threaded if you can, apartment model is a real
headache - you have to marshal your interface pointers between threads
(argh).
You can get more info on threading models in a great KB article on MS's
site:
http://www.microsoft.com/kb/articles/q150/7/77.htm
But like I said, do look at Microsoft Transaction Server - it does all the
thread management *for* you, all you need to do is write an event object
and it will instantiate it for each client in a unique thread....
Please keep me updated with what you come up with... :)
John
Justin Grant <Jus...@FOMCI.com> wrote in article
<01bbf466$bb82be90$83dfb1cc@justin-wnt>...
Sorry, I didn't mean to seem like I was ignoring your suggestion to use
Transaction Server-- it's great technology and I can't wait to use it.
Ditto for free-threading. I also went through weeks of headaches to work
through the kinks of thread-marshalling in interface pointers, because I am
using apartment-threading. Unfortunately, for this project, however, which
is an out-proc server which works in concert with an ActiveX control, both
of which must run on Win95 and be downloadable from a Web page in less than
100K total, my options are severely limited. However, I don't have to
worry about some things, for instance, DCOM, since the application in
question-- an animation server which services multiple controls-- would not
make sense to be run remotely. In addition, the implementation does not
have to be mission-critical or "bulletproof" (as a real "server" app would
be), but merely be fairly resistant to crashing or hanging the user's Web
Browser (which, for me, crashes and hangs anyway, even without my software
loaded!)
I've taken your approach (creating a thread for each direction of
communication, and creating one thread per client) to heart and will be
implementing some combination of the two; I'll keep you posted on how it
turns out.
In the meantime, I have one more stumper for you: if an interface pointer
is marshalled between one apartment (thread) and another, and the second
apartment's thread makes a call (via connection point) back to the client,
then do both server threads' message queues wait for the return of that
call, or does just the thread that the pointer was marshalled into? ( I
think that my problem here is that I'm too ignorant of the actual mechanism
by which pointers are marshalled from one apartment to another; if I knew
more about the nuts-&-bolts of this marshalling operation then I'm sure the
answer would be obvious.)
Thanks again for your answers,
Justin
John Wood <ten...@msn.com> wrote in article
<01bbf4c8$4496f140$aa4e95c1@snake>...
Whoops, I wasn't specific enough in my question. Sorry about that. What I
meant to ask was about the following scenario: what happens in the case
where, on the server, I've marshalled an interface pointer, pointing to the
client's "event sink", from server thread X (which created the object and
implements the IConnectionPointContainer) to server thread Y (which sends
the "events" back to the client). When Server thread "Y" calls a member
function of the marshalled interface pointer, do both threads X and Y block
while the call is executing, or does only thread Y block? In other words,
once an interface pointer is marshalled across threads (apartments), is
there any connection left between the original thread and the marshalled
pointer in the new thread?
Justin
John Wood <ten...@msn.com> wrote in article
<01bbf682$05605000$d74995c1@snake>...
> Justin Grant <Jus...@FOMCI.com> wrote in article
> <01bbf678$cad75a40$83dfb1cc@justin-wnt>...
>
> > In the meantime, I have one more stumper for you: if an interface
> pointer
> > is marshalled between one apartment (thread) and another, and the
second
> > apartment's thread makes a call (via connection point) back to the
> client,
> > then do both server threads' message queues wait for the return of that
> > call, or does just the thread that the pointer was marshalled into?
>
> As I'm sure you know - in apartment model marshaling, the message queue
is
> there both for synchronisation and for cross process communication. In an
> in-process scenario, it is simply there to guard against multiple paths
in
> one instantiation of the COM library - on 3.51 and 95 the COM libraries
are
> not suited to multi-threading, and thus this mechanism is adopted for
> synchronisation.
>
> Every apartment will have a single message queue, and this is primarily
> used for the synchronisation of *incoming* calls, rather than outgoing -
it
> has no need to synchronise outgoing calls because by definition an
> apartment contains a single thread (in apartment model threading at
least),
> and thus can only make one call at a time.
>
> Thus if, in an apartment, you make a call externally - COM will call the
> instantiation of the proxy to marshal the data, and this will end up
> running a SendMessage - and as you know, SendMessage will effect the
> recipient's message queue, but not the sender.
>
> So to answer your question...
>
> If the server makes a call to the client *asynchronously* (that is, the
> call is made while the client is ideally in an idle state), then the
> marshal message will be dispatched from the server without intervention
by
> the server's message queue, and this call will wait for the recipient's
> message queue to return. During this time, the source's thread is *in*
the
> message queue, and is thus unable to receive further calls from anyone.
> ie. To receive calls - that message queue MUST be pumped!!
>
> If, in the scenario you suggest, the server makes a call to the client
> asynchronously and, while waiting for the server to return from its
message
> queue processing, the client attempts to make another call to the
server's
> thread which is calling it - then you hit this problem, SendMessage will
> eventually timeout in Win32 because the server's message queue isn't
being
> pumped... and during this wait for timeout, you hit the deadlock
scenario
> whereby the server is waiting for the client to return, and the client is
> waiting for the server to return - ergo 2 thread's have crashed. Luckily
> RPC traps this with the error you saw.
>
> So - to answer your question, yes - synchronous calls kill the source of
> the call for the duration of the client processing in a single threaded
> environment, and thus deadlocks both the client and server's threads.
>
> Hope this answers your question...
>
> Good luck ;)
>
> John
>
>
As to gaining control over which specific destination thread you wish
to enter into, perhaps you could replace this HTASK with that of your
target thread ID? Just a thought (all trials at your own risk :))
- Lakshmi N.
In message <01bbf689$85573ce0$83dfb1cc@justin-wnt>
"Justin Grant" <Jus...@FOMCI.com> posts:
>It's easy to see how to control the thread context (i.e. message queue &
>apartment) that outgoing interfaces use, because you can just make sure the
>COM method call happens within that thread's context. But how can you
>control the thread from which INCOMING interfaces are handled? Clients
>call a server in the main thread of the apartment, right? (the first
>thread to call CoInitialize, I presume, determines where incoming interface
>calls are handled) How can you force clients to call the server in the
>context of a different thread? (This would be necessary if I wanted to
>implement a separate thread (or 2) for each client).
> What I
> meant to ask was about the following scenario: what happens in the case
> where, on the server, I've marshalled an interface pointer, pointing to
the
> client's "event sink", from server thread X (which created the object and
> implements the IConnectionPointContainer) to server thread Y (which sends
> the "events" back to the client). When Server thread "Y" calls a member
> function of the marshalled interface pointer, do both threads X and Y
block
> while the call is executing, or does only thread Y block? In other
words,
> once an interface pointer is marshalled across threads (apartments), is
> there any connection left between the original thread and the marshalled
> pointer in the new thread?
That's a good question. It depends on what method you are using to marshal
the interface - if you've written your own proxy and stub, then I couldn't
really tell you the answer - and i'm sure you'd know anyway. If you're
using, as I assume you are, CoMarshalThreadblahblah then the proxy it
creates is simply something which carries execution RPC instructions from
one thread to another via the message queue. It doesn't re-instantiate the
object in a new apartment, if you think *this* is hurting your head,
imagine how difficult *that* would be ;)
That caught me when I first started learning this stuff - for a few minutes
I was even stupid enough to think that I could multithread a VB class by
using COM's default interface marshaler ;) But alas... what's in a thread,
stays in a thread - COM just finds a way of commanding it remotely.
So to answer your question!! Thread X will call process the message queue
in winproc A, winproc A will call the proxy for the object and wait for it
to return. The proxy sends a message to Thread Y, Thread Y calls the proxy
for the client - and its SendMessage times out because it is deadlocked
waiting for X to return.
The trick - obviously, is for the client to POST itself a user message and
then return from the event ASAP without doing anything - and then use the
handling of the user message to actually communicate with the server.
If you're not worried about job or request clashes and server performance,
you could even get away with not using threads in the server at all...
heh...
Hope this DOES answer your prob,
John
I'm sure you can appreciate how useful it is knowing how this stuff works
;)
John
Justin Grant <Jus...@FOMCI.com> wrote in article
<01bbf689$85573ce0$83dfb1cc@justin-wnt>...
> Sorry for the bother, but here's one additional question:
>
> It's easy to see how to control the thread context (i.e. message queue &
> apartment) that outgoing interfaces use, because you can just make sure
the
> COM method call happens within that thread's context. But how can you
> control the thread from which INCOMING interfaces are handled? Clients
> call a server in the main thread of the apartment, right? (the first
> thread to call CoInitialize, I presume, determines where incoming
interface
> calls are handled) How can you force clients to call the server in the
> context of a different thread? (This would be necessary if I wanted to
> implement a separate thread (or 2) for each client).
>
> I don't know about you, but too much thinking about this stuff makes my
> head hurt!
>
> Justin
>
Light-Bulb # 1: AHA!
Now I understand what you were saying before about creating two threads per
client. When you said before to create two threads per client, one in each
direction of communication, I thought you meant two threads per client in
the server's process, one for incoming and one for outgoing interfaces. Now
I realize that you meant one thread ON THE CLIENT for outgoing interfaces
from client to server and one thread ON THE SERVER for outgoing interfaces
from server to client. Aha! I was thinking of all my thread-creation on
the server and I just couldn't understand how you could coerce a client to
call your objects within a certain thread context on the server. I
understand how you interpreted my question above to be asking about
multiple message queues per apartment, but I really just wanted to know how
to do the "2-thread-per-communication-channel" thing.
Lack of Light Bulb #2
Further refining the question:
> If you're
> using, as I assume you are, CoMarshalThreadblahblah then the proxy it
> creates is simply something which carries execution RPC instructions from
> one thread to another via the message queue.
Yup, I'm using the ThreadMarshallBlahBlah functions.
>It doesn't re-instantiate the
> object in a new apartment,
In my situation, however, the only "objects" that I'm marshalling between
threads on the server are proxies to interfaces (IUnknown and the event
sink) of the client. So is the Thread-Unmarshalled pointer a pointer to
the original proxy (of which calling will require use of the original
thread's message queue) or is it a direct proxy to the out-proc object
(which won't interfere with the other server thread's message queue when I
call its members)?
> If you're not worried about job or request clashes and server
performance,
> you could even get away with not using threads in the server at all...
> heh...
I wish. The server only needs threads for one purpose: to have realtime
performance on animations on the server side. Everything else is
message-based. If I had known at the start that using threads like this
would have been so much hassle, I would've convinced the client of another
approach! ;-)
>
> Hope this DOES answer your prob,
>
> John
>
Anytime you get sick of my pesky questions, please let me know-- your
assistance has been invaluable so far, but I don't want to exhaust your
patience with me!
Justin
When you unmarshal the pointer, you're creating a proxy. The idea of a
proxy is that it is a clone of the original object's interface, just that
its implementation carries the calls somewhere else. Therefore when you
proxy the object, as far as the marshaling function is concerned - it's
simply being passed an object with which it can create a buffer for
marshaling - if the original object is a proxy in itself, then all you're
going to be doing is creating a proxy to a proxy - it may seem a bit crap
that the process can't be optimised, but sometimes you may want this
"feature". So ya, the call will require the use of the original message
queue.
This is based on some preliminary investigation I did on this about 6
months ago - i'm 90% positive that this is the case anyway, if i wasn't on
vacation right now I'd look into it for a few minutes and make that 100%
but.... it's not the fact I'm on vacation that's stopping me - just that I
haven't got VC with me!! ;)
John
1) Object A is a proxy?
2) If not, then create a proxy to obect A and return
3) If so, query object A for the "home" of the object it's a proxy for.
4) Create a proxy to the "home" object. The resulting proxy will be, more
or less, a clone of the first proxy.
Consider also the possibility for havoc if you ThreadMashallBlahBlah() a
pointer over and over again through seperate threads. If marshalled it in
thread X, unmarshalled it in thread Y, re-marshalled it in thread Y, and
unmarshalled it in thread X, you'd have a deadlock due to the circular
messaging required.
I'm assuming that proxies to proxies are not created, at least not by the
CoMarshalThreadXXX calls.
Justin
John Wood <ten...@msn.com> wrote in article
<01bbf731$eeb94cc0$690fc881@snake>...
> Thinking about it, I'm even more curious now as to whether I am right in
> the assumption that it will in fact create a proxy to a proxy.
>
> Consider a DCOM scenario:
>
> We have machines A and B. Machine A creates object O1. Machine A sends
O1
> to machine B (it creates a proxy, sends the message, machine B creates
the
> stub).
>
> Machine B then sends marshaled O1 and sends it to machine C. Machine C
> then calls a function on O1 - in my theory, this would involve
> communication with machines C, B and then A.
>
> What would then happen if O1 is then sent back to machine A where it is
> stored, and perhaps sent on to machine B again in the future - we could
end
> up having 100 odd TCP packets being sent around 100 different machines
just
> to make 1 darn call !!!
>
> And then what would happen if machine number 62 reboots? Then the call
on
> machine 15 will fail, and it will be incredibly difficult to work out
why.
>
> If this is the case, then I guess you would have to make sure you didn't
> store a pointer to the proxy anywhere in your code, and copy objects as
you
> come across them....
>
> Perhaps you could try it for me quickly in VB or something?
>
> John
>
>
>
I, too, was thinking that perhaps they implemented their proxy-proxy
creation using the method you outlined. However, think what would be
involved in implementing that. I can't think of a COM interface which
encapsulates the information the umarshaler would be looking for - getting
some kind of pointer to the original object? Hmmmm, unless it's some kind
of internal DCOM interface, but i certainly haven't seen it mentioned when
it comes to inter-apartment or cross process marshaling.
And yes, it would be interesting as this "intelligent" proxy creator would
have to know whether the recipient machine will have access to the original
server - or perhaps it has to have to work? And I agree, that deadlock
scenario could occur easily and unexpectantly in cross thread or cross
process marshaling.
Hmmm... i'll ask Dean or someone at MS, and try it out when I get back home
(monday).... or perhaps you could try it out and let me know asap.. thanks
;)
John
The proxy would have to implement custom marshalling. I gleaned the
paragraph below from the IMarshall Books Online entry, as one of a list of
"situations in which to implement custom marshalling":
Objects that themselves are proxy objects can use custom marshaling to
avoid creating proxies to proxies. Instead, the existing proxy can refer
new proxies back to the original object. This capability is important for
the sake of both efficiency and robustness.
> And yes, it would be interesting as this "intelligent" proxy creator
would
> have to know whether the recipient machine will have access to the
original
> server - or perhaps it has to have to work? And I agree, that deadlock
> scenario could occur easily and unexpectantly in cross thread or cross
> process marshaling.
>
> Hmmm... i'll ask Dean or someone at MS, and try it out when I get back
home
> (monday).... or perhaps you could try it out and let me know asap..
thanks
> ;)
>
I'm going to try to put together a simple test of the thread-to-thread
dealock case tomorrow, and I'll tackle the out-proc version if I have time.
Justin
P.S. - Your gung-ho-ness for the Transaction Server has not been ignored.
I'm working with it now and I expect it to be the key to implementing a
project I'm starting soon, where have to pump up the performance of a
web-based client/server solution.
P.P.S. - What do you do when you're not on vacation? I'm impressed with
your level of knowledge and your willingness to help; you must be an asset
to your company.
John Wood <ten...@msn.com> wrote in article
<01bbf708$b7733380$e44c95c1@snake>...
>...
Thanks again for the info.
Justin
Keith Brown <Keith...@msn.com> wrote in article
<01bbf937$97a65b00$49682299@pc-1>...
It's good to hear you're looking at Microsoft Transaction Server - the
project i'm starting when I get back will orientate around this, and it'll
be good to know some others who are using it in different projects. I've
used some of my spare time on vacation to look at the feasibility of a few
of these technlogies - interestingly one of the things on my list was a
full-duplex communication over DCOM test... I've also been looking at
Microsoft Message Queueing Server and OLE/DB.
I work for a software called "royalblue corporation" - who, among other
things, provide software for financial institutions. I've just finished
leading an equities trading system for a major merchant bank, and the next
project is something similar - just using these new technologies (phasing
them in at least).
Finding people with the skills to implement this stuff is incredibly
difficult - we've been interviewing people for months now, and it's
difficult enough finding someone who knows what a class factory is, and
often when do it's only because they read it an hour before the interview
;) Your skill set is the blueprint for what all companies are in
desperate need of in their development groups, and what is lacking in the
industry at the moment.
What do you do, and what kind of projects are you working on?
Thanks for the interesting thread,
John
Justin Grant <Jus...@FOMCI.COM> wrote in article
<01bbf952$f15c1980$8cdfb1cc@justinlt>...
John Wood <ten...@msn.com> wrote in article
<01bbf97c$6f4b7e20$1f4d95c1@snake>...
Keith Brown <Keith...@msn.com> wrote in article
<01bbfa09$52b40600$c44d2299@pc-1>...
Yes the proxy supports IMarshal. IMarshal is the proxy's initialization
interface (CoUnmarshalInterface creates the proxy via
CoCreateInstance(<proxy clsid> ... IID_IMarshal ...) then calls
IMarshal::UnmarshalInterface() passing the marshaling packet.
If you use strictly standard marshaling, (MIDL generated proxy/stub code),
you get the OBJREF stuff for free, and no proxies to proxies.
On the other hand, if you do custom marshaling, you have taken
responsibility for the maintenance of the object's identity. You must
implement IMarshal::MarshalInterface(), and you'll fill the marshaling
packet with whatever information is necessary for a new proxy to find the
original object. Unless of course a proxy-to-proxy makes sense for some
reason. With custom marshaling, you're completely in charge.
If you're interested in getting a solid understanding of how this stuff
works, check out DevelopMentor's Optimizing DCOM class:
http://www.develop.com.