-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathSSYInterappServer.h
174 lines (143 loc) · 7.01 KB
/
SSYInterappServer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#import <Cocoa/Cocoa.h>
#import "SSYInterappServerDelegate.h"
extern NSString* const SSYInterappServerErrorDomain ;
#define SSYInterappServerErrorFailedToCreatePort 287101
#define SSYInterappServerErrorPortNameAlreadyInUse 287102
#define SSYInterappServerErrorFailedToInitializeSelf 287103
@class SSYInterappServer ;
/*!
@brief A server object which responds to a message sent by an
SSYInterAppClient object from another thread or process.
@details If the headerByte sent by the SSYInterAppClient is 0, this server
will swallow the message but will not respond. This is useful in case the
client just wants to see if the server port is present on the system. (If
not, the client will get a transmit error.)
Dave Keck has suggested an easier way to do this.
See comment at bottom of this file.
Troubleshooting tip. To see if a port is active in the system,
* On macOS 10.9 or earlier
* sudo launchctl bstree | grep <fragmentOfYourPortName>
* Example:
* sudo launchctl bstree | grep Extore
* On macOS 10.10 or later
* sudo launchctl print user/<uid> | grep <fragmentOfYourPortName>
* Example:
* sudo launchctl print user/501 | grep Extore
Here are some search terms, so I can find the above comment later:
command line, active mach port, active mach ports,
*/
__attribute__((visibility("default"))) @interface SSYInterappServer : NSObject {}
/*!
@brief The userInfo which is set in +leaseServerWithPortName::::.
@details The getter is typically used to retrieve the receiver's
userInfo in the delegate method -interappServer:didReceiveHeaderByte:data:.
The setter is typically used to re-purpose the receiver to handle a different
message.
*/
@property (retain) NSDictionary* userInfo ;
/*!
@brief An object to which will be sent Objective-C messages whenever
an interapp message is received by the receiver from a SSYInterAppClient.
@details The receiver does not retain the delegate.
*/
@property (assign, nonatomic) NSObject <SSYInterappServerDelegate> * delegate ;
/*!
@brief Returns a server object which has given port name on the system
@details This method takes care of the fact that only one port with a
given name may exist on the system. It should be sent whenever a delegate
needs to use a server, and re-sent whenever there is any possibility that
the server may have been leased to a different delegate. To determine if
this has happened, the delegate should do something like this…
if ([[self server] delegate] == self) {
// Send leaseServerWithPortName:::: again
...
}
@param portName An arbitrary name you supply, which must be unique among
CFMessagePorts on the computer. Suggest including a reverse DNS identifier.
@param delegate See declaration of the 'delegate' property. If an
existing server is returned by this method, its delegate will be overwritten
with this value.
@param userInfo A dictionary which may be used to pass information to the
delegate. If an existing server is returned, its userInfo will be
overwritten with this value.
@param error_p If the receiver cannot be initialized, will point to an
error object describing the problem. You may pass NULL if uninterested in this.
@result If you have previously leased a server with the given name during
the course of the current process, and if the lease is still active, the
number of leases will be incremented and that same old server will be
returned. Otherwise, a new server will be created.
*/
+ (SSYInterappServer*)leaseServerWithPortName:(NSString*)portName
delegate:(NSObject <SSYInterappServerDelegate> *)delegate
userInfo:(NSDictionary*)userInfo
error_p:(NSError**)error_p ;
/*!
@brief Informs the receiver's class that you are done with this instance.
@details Removes a given delegate from any of the receiver's servers.
This message should be sent, at least, when the delegate is deallocated,
and maybe sooner.
Also, when the number of leases on a server with a given port name falls
to 0, the server and its underlying CFMessage port will be invalidated,
released, and, eventually, deallocated.
The delegate: parameter was added in BookMacster 1.11 to eliminate crashes
which occurred when this happened:
• +leaseServerWithPortName:delegate::: assigns Delegate A to Server 1
• +leaseServerWithPortName:delegate::: assigns Delegate B to Server 1
• Delegate B is deallocated
• Delegate A thinks that it is still assigned to Server 1 and does something which
causes client to send IPC messages to Server 1, or maybe something goes haywire
in the client and it sends an IPC message for some spurious reason
• Server 1 receives an IPC message, processes it and sends a message to its
deallocated delegate, Delegate B, which causes a crash
With this fix, in Delegate B's -dealloc method, when it sends this message, Server 1
will now have nil delegate, so that it will not send a message to its deallocated
delegate, Delegate B.
However, although it won't crash, it will now be a no-op. That's better, but still
no go. To complete the fix, delegates must check with their server when there
is any possibility that the server may have been leased to a different delegate,
as explained in details of -leaseServerWithPortName::::.
*/
- (void)unleaseForDelegate:(NSObject <SSYInterappServerDelegate> *)delegate ;
@end
#if 0
// Dave Keck suggests that this can be done easier using Cocoa…
// From: http://pastie.org/1435791
// He implies sthat he tested it in one process with two threads
// but says it should work equally well if split into two processes…
#import <Foundation/Foundation.h>
@interface MyObject : NSObject
@end
@implementation MyObject
+ (void)serverThread
{
[[NSAutoreleasePool alloc] init];
NSMachPort *serverPort = (id)[NSMachPort port];
[serverPort setDelegate: (id)self];
[serverPort scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
assert([[NSMachBootstrapServer sharedInstance] registerPort: serverPort name: @"Jerry's Server"]);
[[NSRunLoop currentRunLoop] runUntilDate: [NSDate distantFuture]] ;
[pool drain] ;
}
+ (void)handlePortMessage: (NSPortMessage *)message
{
NSLog(@"Received message (msgid: 0x%X): %@", (long)[message msgid], message);
}
@end
int main(int argc, const char *argv[])
{
[[NSAutoreleasePool alloc] init];
[NSThread detachNewThreadSelector: @selector(serverThread) toTarget: [MyObject class] withObject: nil];
sleep(1);
NSMachPort *clientPort = (id)[[NSMachBootstrapServer sharedInstance] portForName: @"Jerry's Server"];
assert(clientPort);
for (;;)
{
NSPortMessage *message = [[[NSPortMessage alloc] initWithSendPort: clientPort receivePort: nil
components: [NSArray arrayWithObject: [@"hello" dataUsingEncoding: NSUTF8StringEncoding]]] autorelease];
[message setMsgid: 0xCAFEBABE];
assert([message sendBeforeDate: [NSDate distantFuture]]);
sleep(1);
}
return 0;
}
#endif