Why is IPCSocket.DataAvailable in My Thread?

I have an instance of Task running in my app. At times it calls IPCSocket.Write. I’m really surprised, however, to find that when incoming IPC data fires the socket’s DataAvailable event that that event’s code is in my thread! Here’s the stack trace:

What the heck is it doing in the thread? I didn’t call it from the thread - the message came from my helper app, some tens or hundreds of milliseconds after I wrote to the socket in SendIPCMessage.

Your PC (or Mac) is not a Real TIme OS. So you may get events out-of-order, especially if they are of short duration, delivered over a high bandwidth link (such as IPC) and occur closely spaced in time. Your thread evidently persists enough for the DataAvailable to be incorporated in its lifetime.
Regards, Tony Barry
Sydney, Australia

The thread persists for a long time, like 30 seconds. My understanding, however, is that only code called from a thread is executed in the thread, not any event that happens to occur while the thread is running, as you’re suggesting. Am I mistaken? Are you saying that if I press a button while a thread is running that that button’s Action event code executes in the thread? I don’t think so.

If you declare the IPC socket in your thread, (to talk out on) then I am fairly confident that an event associated with that socket (in this case, DataAvailable) is also going to be provided to you by the framework. What you do with it is of course your choice,.

Regards, Tony Barry
Sydney, Australia

The socket is instantiated in the main thread when the app first launches, long before the thread ever runs. When the thread isn’t running, IPCSocket.DataAvailable runs in the main thread as expected (where else could it run, lol). Sorry, I probably should have mentioned that in my original post.

I should further mention that the helper app is sending IPC data more or less continuously - a few dozen bytes every couple of hundred milliseconds - so I think it’s entirely possible that DataAvailable can happen simultaneously with Write. But I still doubt that it’s expected behavior for DataAvailable to “jump the fence” and suddenly run itself in the thread.

Hmmm. I think that events are available to every thread (main or alternates) that has a reference to them, as they are provided by the framework.

I note that you are surprised to see a dataAvailable event occur in your thread, where you had not expected it. I gather that you want the dataAvailable event to fire in the main thread only. More knowledgeable folk might have a more elegant answer than mine, but here it is …

Accept that the framework fires the event into your thread, despite your wishes to the contrary; and then use that event to set a boolean available to the main thread to tell the main thread to execute your handled code.

Note that this implies that you clone the IPCsocket by default when you create the thread, which makes me wonder if you actually have two IPC sockets not one.

Regards,
Tony Barry

I’ll file that under “possible workarounds”, thanks :slight_smile:

That seems a little farfetched. How could two sockets share a single IPC temp file path, and how could my helper app’s single IPCSocket deal with talking simultaneously to two sockets at the other end? My socket is a property of a module, and I’m pretty sure there’s only one, instantiated, as I mentioned, once and only once on app launch. The thread/task is a control dragged into a window in the IDE.

Certainly. Feel free to accept, ignore, etc. as you wish.

I appreciate that you may consider the compiler is not doing as you wish, but it is unfortunately the entity you have to satisfy, not the other way around.

Your observation that the compiler fires a dataAvailable event in your thread rather than in the main thread strongly implies that your thread owns the IPC socket. From this, I infer that there might be two sockets. Other possibilities do not come to mind. The chance that this is a compiler bug is not great. Bugs cause BSOD etc. This seems to be an instantiation issue, perhaps due to the way you create the thread.

However feel free to discuss with others.

Best wishes.
Tony Barry
Sydney, Australia

Thanks, Tony. I’m certainly willing at some point to accept and work around, but engineering discipline dictates a best-efforts search for root cause to gain as much understanding of the problem as possible before applying a band-aid to the symptoms. I dislike mysteries in product development - left unsolved, they usually come back to bite. Could this not be caused by a bug or quirk in the socket code? Perhaps there are timing issues involved. Or maybe it’s something I’m doing, but in the creation of the thread at least, I’m not doing anything out of the ordinary - the Task object has been instantiated by dragging it onto a window from the Library, and its Run method is simply called from the window code.

If no one else has any thoughts on it, maybe I’ll try to replicate the issue in a small example program.

I’m probably wrong, but hopefully someone more knowledgeable about IPC sockets can chime in on this. Here’s what I’m thinking:

This may be why it is firing in your thread. Modules are not actually instantiated with scope like a regular object - they are globals, available everywhere to all threads - main and otherwise. When an event is triggered, it’ll be triggered in the scope of the object that is receiving the event. In the case of a module, this means it can happen literally anywhere in your app, at any time. When the event loop fires, if there is an event to raise, it’ll happen no matter what thread the event loop is in.

Have you considered moving the socket into a class that you instantiate in the main thread and keep as a property there, rather than in a global scope? I’m just spitballin’ here, but I’d start by not having the socket be in a global scope.

Thanks, Kimball, but scope of objects has nothing to do with what thread code will run in. The only thing that determines (theoretically) what thread code runs in is the thread that that code was called from. Any code called from a thread runs in that thread. Any code called from the main thread runs in the main thread.

Hmmm. An event is not called as such by your code. It is delivered by the framework. So it makes sense to me that your thread will receive a dataAvailable from an IPCsocket that it knows about.

However, you are in front of your code and I am not, so I wish you the best in sorting out the issue. Please feel free to correspond if you have a victory in the battle. All of us will benefit if we can know the outcome.

Regards, Tony Barry
Sydney, Australia

Are you polling the socket from within the thread?

This is true when you are calling the code. Not so much when the event loop is triggering things.

Those more familiar with the event loop model that Xojo employs can speak to the specifics, but the event loop is raising the dataAvailable event in that socket. That socket is global to your app, thus when the event loop runs in the thread it will service events for everything in the thread as well as global sockets.

Of course, I’m mostly speaking in hypotheticals here, as I don’t know much about how / where / when / etc events are triggered.

I do think you should try changing the socket to live in an instantiated object that is scoped to your main thread rather than letting it live in global scope. I do a lot of thread and socket programming, but I keep my sockets organized by the thread that needs them. I never put them in global scope.

My understanding is that socket / IPC events should all be called on the main thread. I have seen weird things happen in the past if polling is done in a thread so you may want to check for that. You may have found a bug so I would try to log a feedback report with a project that demonstrates the problem.

Socket events should normally occur on the man thread only, with the exception of polling the socket. That could cause a more “procedural” processing of the DataAvailable event, making it part of the thread.

Same issue as this? <https://xojo.com/issue/12563>
(note the title is wrong, it also happens in built apps)

I’m not polling the socket, but I see now that it seems to be IPCsocket.Flush that causes DataAvailable to fire.

Now that I know it’s a known issue, thanks to @Michael Diehr, I will figure out a workaround.

Thanks all for your help and comments.

Julia

Not polling, but I am flushing it from within the thread.