Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sending messages to a thread?

I need to imlement in cocoa, a design that relies on multiple threads.

I started at the CoreFoundation level - I created a CFMessagePort and attached it to the CFRunLoop, but it was very inconvenient as (unlike on other platforms) it needs to have a (systemwide) unique name, and CFMessagePortSendRequest does not process callbacks back to the current thread while waiting. Its possible to create my own CFRunLoopSource object, but building my own thread safe queue seems like overkill.

I then switched from using POSIX threads to NSThreads, calling performSelector:onThread: to send messages to other threads. This is far easier to use than the CFMessagePort mechanism, but again, performSelector:onThread: does not allow the main thread to send messages back to the current thread - and there is no return value.

All I need is a simple - inprocess - mechanism (so I hopefully don't need to invent schemes to create 'unique' names) that lets me send a message (and wait for a reply) from thread A to thread B, and, while waiting for the message, allow thread B to send a message (and wait for a reply) to/from thread A.

A simple: A calls B re-entrantly calls A situation that's so usual on a single thread, but is deadlock hell when the messages are between threads.

like image 357
Chris Becke Avatar asked Oct 30 '25 12:10

Chris Becke


1 Answers

use -performSelectorOnThread:withObject:waitUntilDone:. The object you pass would be something that has a property or other "slot" that you can put the return value in. e.g.

SomeObject* retObject = [[SomeObject alloc] init];
[anotherObject performSelectorOnThread: whateverThread withObject: retObject waitUntilDone: YES];
id retValue = [retObject retValue];

If you want to be really sophisticated about it, instead of passing an object of a class you define, use an NSInvocation object and simply invoke it on the other thread (make sure not to invoke the same NSInvocation on two threads simultaneously) e.g.

[invocation performSelectorOnMainThread:@selector(invoke) withObject:NULL waitUntilDone:YES];

Edit

if you don't want to wait for the processing on the other thread to complete and you want a return value, you cannot avoid the other thread calling back into your thread. You can still use an invocation e.g.

[comObject setInvocation: myInvocation];
[comObject setCallingThread: [NSThread currentThread]];
[someObject performSelectorOnMainThread: @selector(runInvocation:) withObject: comObject waitUntilDone: NO];

// in someObject's implementation

-(void) runInvocation: (ComObject*) comObject
{
    [[comObject invocation] invoke];
    [self perfomSelector: @selctor(invocationComplete:) 
                onThread: [comObject callingThread] 
              withObject: [comObject invocation]];
}

If you don't like to create a new class to pass the thread and the invocation, use an NSDictionary instead e.g.

comObject = [NSDictionary dictionaryWithObjectsAndKeys: invocation, "@invocation" [NSThread currentThread], @"thread", nil];

Be careful about object ownership. The various performSelector... methods retain both the receiver and the object until they are done but with asynchronous calls there might be a small window in which they could disappear if you are not careful.

like image 130
JeremyP Avatar answered Nov 02 '25 03:11

JeremyP



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!