rb-scpt 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/rb-scpt-1.0.1.gem +0 -0
- data/extconf.rb +12 -12
- data/rb-scpt.gemspec +10 -10
- data/sample/AB_export_vcard.rb +16 -16
- data/sample/AB_list_people_with_emails.rb +4 -4
- data/sample/Add_iCal_event.rb +12 -12
- data/sample/Create_daily_iCal_todos.rb +28 -28
- data/sample/Export_Address_Book_phone_numbers.rb +52 -52
- data/sample/Hello_world.rb +10 -10
- data/sample/List_iTunes_playlist_names.rb +3 -3
- data/sample/Make_Mail_message.rb +24 -24
- data/sample/Open_file_in_TextEdit.rb +5 -5
- data/sample/Organize_Mail_messages.rb +46 -46
- data/sample/Print_folder_tree.rb +5 -5
- data/sample/Select_all_HTML_files.rb +6 -6
- data/sample/Set_iChat_status.rb +12 -12
- data/sample/Simple_Finder_GUI_Scripting.rb +6 -6
- data/sample/Stagger_Finder_windows.rb +9 -9
- data/sample/TextEdit_demo.rb +71 -71
- data/sample/iTunes_top40_to_html.rb +28 -30
- data/src/SendThreadSafe.c +293 -293
- data/src/SendThreadSafe.h +108 -108
- data/src/lib/_aem/aemreference.rb +997 -998
- data/src/lib/_aem/codecs.rb +609 -610
- data/src/lib/_aem/connect.rb +197 -197
- data/src/lib/_aem/encodingsupport.rb +67 -67
- data/src/lib/_aem/findapp.rb +75 -75
- data/src/lib/_aem/mactypes.rb +241 -242
- data/src/lib/_aem/send.rb +268 -268
- data/src/lib/_aem/typewrappers.rb +52 -52
- data/src/lib/_appscript/defaultterminology.rb +266 -266
- data/src/lib/_appscript/referencerenderer.rb +230 -233
- data/src/lib/_appscript/reservedkeywords.rb +106 -106
- data/src/lib/_appscript/safeobject.rb +125 -125
- data/src/lib/_appscript/terminology.rb +448 -449
- data/src/lib/aem.rb +238 -238
- data/src/lib/kae.rb +1487 -1487
- data/src/lib/osax.rb +647 -647
- data/src/lib/rb-scpt.rb +1065 -1065
- data/src/rbae.c +595 -595
- data/test/test_aemreference.rb +104 -107
- data/test/test_appscriptcommands.rb +131 -134
- data/test/test_appscriptreference.rb +96 -99
- data/test/test_codecs.rb +166 -168
- data/test/test_findapp.rb +13 -16
- data/test/test_mactypes.rb +70 -72
- data/test/test_osax.rb +46 -48
- data/test/testall.sh +4 -4
- metadata +8 -7
data/src/SendThreadSafe.c
CHANGED
@@ -1,72 +1,72 @@
|
|
1
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
|
-
$Log: AESendThreadSafe.c,v $
|
55
|
-
Revision 1.3 2007/02/27 10:45:15
|
56
|
-
In the destructor, add an assert that the thread-local storage has been set to NULL. Also fixed a comment typo.
|
57
|
-
|
58
|
-
Revision 1.2 2007/02/12 11:59:09
|
59
|
-
Added a type cast for the malloc result.
|
60
|
-
|
61
|
-
Revision 1.1 2007/02/09 10:55:24
|
62
|
-
First checked in.
|
2
|
+
File: AESendThreadSafe.c
|
3
|
+
|
4
|
+
Contains: Code to send Apple events in a thread-safe manner.
|
5
|
+
|
6
|
+
Written by: DTS
|
7
|
+
|
8
|
+
Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved.
|
9
|
+
|
10
|
+
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
11
|
+
("Apple") in consideration of your agreement to the following
|
12
|
+
terms, and your use, installation, modification or
|
13
|
+
redistribution of this Apple software constitutes acceptance of
|
14
|
+
these terms. If you do not agree with these terms, please do
|
15
|
+
not use, install, modify or redistribute this Apple software.
|
16
|
+
|
17
|
+
In consideration of your agreement to abide by the following
|
18
|
+
terms, and subject to these terms, Apple grants you a personal,
|
19
|
+
non-exclusive license, under Apple's copyrights in this
|
20
|
+
original Apple software (the "Apple Software"), to use,
|
21
|
+
reproduce, modify and redistribute the Apple Software, with or
|
22
|
+
without modifications, in source and/or binary forms; provided
|
23
|
+
that if you redistribute the Apple Software in its entirety and
|
24
|
+
without modifications, you must retain this notice and the
|
25
|
+
following text and disclaimers in all such redistributions of
|
26
|
+
the Apple Software. Neither the name, trademarks, service marks
|
27
|
+
or logos of Apple Inc. may be used to endorse or promote
|
28
|
+
products derived from the Apple Software without specific prior
|
29
|
+
written permission from Apple. Except as expressly stated in
|
30
|
+
this notice, no other rights or licenses, express or implied,
|
31
|
+
are granted by Apple herein, including but not limited to any
|
32
|
+
patent rights that may be infringed by your derivative works or
|
33
|
+
by other works in which the Apple Software may be incorporated.
|
34
|
+
|
35
|
+
The Apple Software is provided by Apple on an "AS IS" basis.
|
36
|
+
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
37
|
+
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
38
|
+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
39
|
+
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
40
|
+
COMBINATION WITH YOUR PRODUCTS.
|
41
|
+
|
42
|
+
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
43
|
+
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
44
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
45
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
46
|
+
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
47
|
+
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
48
|
+
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
49
|
+
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
50
|
+
SUCH DAMAGE.
|
51
|
+
|
52
|
+
Change History (most recent first):
|
53
|
+
|
54
|
+
$Log: AESendThreadSafe.c,v $
|
55
|
+
Revision 1.3 2007/02/27 10:45:15
|
56
|
+
In the destructor, add an assert that the thread-local storage has been set to NULL. Also fixed a comment typo.
|
57
|
+
|
58
|
+
Revision 1.2 2007/02/12 11:59:09
|
59
|
+
Added a type cast for the malloc result.
|
60
|
+
|
61
|
+
Revision 1.1 2007/02/09 10:55:24
|
62
|
+
First checked in.
|
63
63
|
|
64
64
|
|
65
65
|
*/
|
66
66
|
|
67
67
|
/*
|
68
68
|
|
69
|
-
2007/06/24 -- Modified by HAS to make AESendMessageThreadSafeSynchronous API-compatible with AESendMessage; renamed SendMessageThreadSafe.
|
69
|
+
2007/06/24 -- Modified by HAS to make AESendMessageThreadSafeSynchronous API-compatible with AESendMessage; renamed SendMessageThreadSafe.
|
70
70
|
|
71
71
|
*/
|
72
72
|
|
@@ -80,53 +80,53 @@ First checked in.
|
|
80
80
|
/////////////////////////////////////////////////////////////////
|
81
81
|
|
82
82
|
/*
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
83
|
+
How It Works
|
84
|
+
------------
|
85
|
+
The basic idea behind this module is that it uses per-thread storage to keep
|
86
|
+
track of an Apple event reply for any given thread. The first time that the
|
87
|
+
thread calls AESendMessageThreadSafeSynchronous, the per-thread storage will
|
88
|
+
not be initialised and the code will grab an Apple event reply port and
|
89
|
+
assign it to the per-thread storage. Subsequent calls to AESendMessageThreadSafeSynchronous
|
90
|
+
will continue to use that port. When the thread dies, pthreads will automatically
|
91
|
+
call the destructor for the per-thread storage, and that will clean up the port.
|
92
|
+
|
93
|
+
Because we can't dispose of the reply port (without triggering the Apple
|
94
|
+
Event Manager bug that's the reason we wrote this code in the first place),
|
95
|
+
the destructor doesn't actually dispose of the port. Rather, it adds the
|
96
|
+
port to a pool of ports that are available for reuse. The next time a thread
|
97
|
+
needs to allocate a port, it will grab it from the pool rather than allocating
|
98
|
+
it from scratch.
|
99
|
+
|
100
|
+
This technique means that the code still 'leaks' Apple event reply ports, but
|
101
|
+
the size of the leak is limited to the maximum number of threads that you run
|
102
|
+
simultaneously. This isn't a problem in practice.
|
103
103
|
*/
|
104
104
|
|
105
105
|
/////////////////////////////////////////////////////////////////
|
106
106
|
|
107
|
-
// The PerThreadStorage structure is a trivial wrapper around the Mach port.
|
108
|
-
// I added this because I need to attach this structure a thread using
|
109
|
-
// per-thread storage. The API for that (<x-man-page://3/pthread_setspecific>)
|
110
|
-
// is pointer based. I could've just cast the Mach port to a (void *), but
|
111
|
-
// that's ugly because of a) pointer size issues (are pointers always bigger than
|
107
|
+
// The PerThreadStorage structure is a trivial wrapper around the Mach port.
|
108
|
+
// I added this because I need to attach this structure a thread using
|
109
|
+
// per-thread storage. The API for that (<x-man-page://3/pthread_setspecific>)
|
110
|
+
// is pointer based. I could've just cast the Mach port to a (void *), but
|
111
|
+
// that's ugly because of a) pointer size issues (are pointers always bigger than
|
112
112
|
// ints?), and b) because it implies an equivalent between NULL and MACH_PORT_NULL.
|
113
113
|
// Given this, I simply decided to create a structure to wrap the Mach port.
|
114
114
|
|
115
115
|
enum {
|
116
|
-
|
116
|
+
kPerThreadStorageMagic = 'PTSm'
|
117
117
|
};
|
118
118
|
|
119
119
|
struct PerThreadStorage {
|
120
|
-
|
121
|
-
|
120
|
+
OSType magic; // must be kPerThreadStorageMagic
|
121
|
+
mach_port_t port;
|
122
122
|
};
|
123
123
|
typedef struct PerThreadStorage PerThreadStorage;
|
124
124
|
|
125
|
-
// The following static variables manage the per-thread storage key
|
126
|
-
// (sPerThreadStorageKey) and the pool of Mach ports (wrapped in
|
125
|
+
// The following static variables manage the per-thread storage key
|
126
|
+
// (sPerThreadStorageKey) and the pool of Mach ports (wrapped in
|
127
127
|
// PerThreadStorage structures) that are not currently attached to a thread.
|
128
128
|
|
129
|
-
static pthread_once_t sInited = PTHREAD_ONCE_INIT; // covers initialisation of all of the
|
129
|
+
static pthread_once_t sInited = PTHREAD_ONCE_INIT; // covers initialisation of all of the
|
130
130
|
// following static variables
|
131
131
|
|
132
132
|
static OSStatus sPerThreadStorageKeyInitErrNum; // latches result of initialisation
|
@@ -134,247 +134,247 @@ static OSStatus sPerThreadStorageKeyInitErrNum; // latches result of
|
|
134
134
|
static pthread_key_t sPerThreadStorageKey = 0; // key for our per-thread storage
|
135
135
|
|
136
136
|
static pthread_mutex_t sPoolMutex; // protects sPool
|
137
|
-
static CFMutableArrayRef sPool; // array of (PerThreadStorage *), holds
|
138
|
-
// the ports that aren't currently bound to
|
137
|
+
static CFMutableArrayRef sPool; // array of (PerThreadStorage *), holds
|
138
|
+
// the ports that aren't currently bound to
|
139
139
|
// a thread
|
140
140
|
|
141
141
|
static void PerThreadStorageDestructor(void *keyValue); // forward declaration
|
142
142
|
|
143
143
|
static void InitRoutine(void)
|
144
|
-
|
144
|
+
// Call once (via pthread_once) to initialise various static variables.
|
145
145
|
{
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
}
|
146
|
+
OSStatus err;
|
147
|
+
|
148
|
+
// Create the per-thread storage key. Note that we assign a destructor to this key;
|
149
|
+
// pthreads call the destructor to clean up that item of per-thread storage whenever
|
150
|
+
// a thread terminates.
|
151
|
+
|
152
|
+
err = (OSStatus) pthread_key_create(&sPerThreadStorageKey, PerThreadStorageDestructor);
|
153
|
+
|
154
|
+
// Create the pool of Mach ports that aren't bound to any thread, and its associated
|
155
|
+
// lock. The pool starts out empty.
|
156
|
+
|
157
|
+
if (err == noErr) {
|
158
|
+
err = (OSStatus) pthread_mutex_init(&sPoolMutex, NULL);
|
159
|
+
}
|
160
|
+
if (err == noErr) {
|
161
|
+
sPool = CFArrayCreateMutable(NULL, 0, NULL);
|
162
|
+
if (sPool == NULL) {
|
163
|
+
err = coreFoundationUnknownErr;
|
165
164
|
}
|
166
|
-
|
167
|
-
|
168
|
-
|
165
|
+
}
|
166
|
+
assert(err == 0);
|
167
|
+
|
168
|
+
sPerThreadStorageKeyInitErrNum = err;
|
169
169
|
}
|
170
170
|
|
171
171
|
static OSStatus AllocatePortFromPool(PerThreadStorage **storagePtr)
|
172
|
-
|
172
|
+
// Grab a Mach port from sPool; if sPool is empty, create one.
|
173
173
|
{
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
assert( storagePtr != NULL);
|
179
|
-
assert(*storagePtr == NULL);
|
180
|
-
|
181
|
-
storage = NULL;
|
182
|
-
|
183
|
-
// First try to get an entry from pool. We try to grab the last one because
|
184
|
-
// that minimises the amount of copying that CFArrayRemoveValueAtIndex has to
|
185
|
-
// do.
|
186
|
-
|
187
|
-
err = (OSStatus) pthread_mutex_lock(&sPoolMutex);
|
188
|
-
if (err == noErr) {
|
189
|
-
CFIndex poolCount;
|
190
|
-
|
191
|
-
poolCount = CFArrayGetCount(sPool);
|
192
|
-
if (poolCount > 0) {
|
193
|
-
storage = (PerThreadStorage *) CFArrayGetValueAtIndex(sPool, poolCount - 1);
|
194
|
-
CFArrayRemoveValueAtIndex(sPool, poolCount - 1);
|
195
|
-
}
|
196
|
-
|
197
|
-
junk = (OSStatus) pthread_mutex_unlock(&sPoolMutex);
|
198
|
-
assert(junk == noErr);
|
199
|
-
}
|
174
|
+
OSStatus err;
|
175
|
+
OSStatus junk;
|
176
|
+
PerThreadStorage * storage;
|
200
177
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
178
|
+
assert( storagePtr != NULL);
|
179
|
+
assert(*storagePtr == NULL);
|
180
|
+
|
181
|
+
storage = NULL;
|
182
|
+
|
183
|
+
// First try to get an entry from pool. We try to grab the last one because
|
184
|
+
// that minimises the amount of copying that CFArrayRemoveValueAtIndex has to
|
185
|
+
// do.
|
186
|
+
|
187
|
+
err = (OSStatus) pthread_mutex_lock(&sPoolMutex);
|
188
|
+
if (err == noErr) {
|
189
|
+
CFIndex poolCount;
|
190
|
+
|
191
|
+
poolCount = CFArrayGetCount(sPool);
|
192
|
+
if (poolCount > 0) {
|
193
|
+
storage = (PerThreadStorage *) CFArrayGetValueAtIndex(sPool, poolCount - 1);
|
194
|
+
CFArrayRemoveValueAtIndex(sPool, poolCount - 1);
|
218
195
|
}
|
219
|
-
|
220
|
-
|
196
|
+
|
197
|
+
junk = (OSStatus) pthread_mutex_unlock(&sPoolMutex);
|
198
|
+
assert(junk == noErr);
|
199
|
+
}
|
200
|
+
|
201
|
+
// If we failed to find an entry in the pool, create a new one.
|
202
|
+
|
203
|
+
if ( (err == noErr) && (storage == NULL) ) {
|
204
|
+
storage = (PerThreadStorage *) malloc(sizeof(*storage));
|
205
|
+
if (storage == NULL) {
|
206
|
+
err = memFullErr;
|
207
|
+
} else {
|
208
|
+
storage->magic = kPerThreadStorageMagic;
|
209
|
+
storage->port = MACH_PORT_NULL;
|
210
|
+
|
211
|
+
err = (OSStatus) mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &storage->port);
|
212
|
+
if (err != noErr) {
|
213
|
+
assert(storage->port == MACH_PORT_NULL);
|
214
|
+
free(storage);
|
215
|
+
storage = NULL;
|
216
|
+
}
|
221
217
|
}
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
218
|
+
}
|
219
|
+
if (err == noErr) {
|
220
|
+
*storagePtr = storage;
|
221
|
+
}
|
222
|
+
|
223
|
+
assert( (err == noErr) == (*storagePtr != NULL) );
|
224
|
+
assert( (*storagePtr == NULL) || ((*storagePtr)->magic == kPerThreadStorageMagic) );
|
225
|
+
assert( (*storagePtr == NULL) || ((*storagePtr)->port != MACH_PORT_NULL) );
|
226
|
+
|
227
|
+
return err;
|
228
228
|
}
|
229
229
|
|
230
230
|
static void ReturnPortToPool(PerThreadStorage * storage)
|
231
|
-
|
231
|
+
// Returns a port to sPool.
|
232
232
|
{
|
233
|
-
|
233
|
+
OSStatus err;
|
234
234
|
|
235
|
-
|
236
|
-
|
237
|
-
|
235
|
+
assert(storage != NULL);
|
236
|
+
assert(storage->magic == kPerThreadStorageMagic);
|
237
|
+
assert(storage->port != MACH_PORT_NULL);
|
238
238
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
239
|
+
err = (OSStatus) pthread_mutex_lock(&sPoolMutex);
|
240
|
+
if (err == noErr) {
|
241
|
+
CFArrayAppendValue(sPool, storage);
|
242
|
+
|
243
|
+
err = (OSStatus) pthread_mutex_unlock(&sPoolMutex);
|
244
|
+
}
|
245
|
+
assert(err == noErr);
|
246
246
|
}
|
247
247
|
|
248
248
|
// Main Thread Notes
|
249
249
|
// -----------------
|
250
|
-
// There are two reasons why we don't assign a reply port to the main thread.
|
251
|
-
// First, the main thread already has a reply port created for it by Apple
|
252
|
-
// Event Manager. Thus, we don't need a specific reply port. Also, the
|
253
|
-
// destructor for per-thread storage isn't called for the main thread, so
|
254
|
-
// we wouldn't get a chance to clean up (although that's not really a problem
|
250
|
+
// There are two reasons why we don't assign a reply port to the main thread.
|
251
|
+
// First, the main thread already has a reply port created for it by Apple
|
252
|
+
// Event Manager. Thus, we don't need a specific reply port. Also, the
|
253
|
+
// destructor for per-thread storage isn't called for the main thread, so
|
254
|
+
// we wouldn't get a chance to clean up (although that's not really a problem
|
255
255
|
// in practice).
|
256
256
|
|
257
257
|
static OSStatus BindReplyMachPortToThread(mach_port_t *replyPortPtr)
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
258
|
+
// Get a reply port for this thread, remembering that we've done this
|
259
|
+
// in per-thread storage.
|
260
|
+
//
|
261
|
+
// On success, *replyPortPtr is the port to use for this thread's reply
|
262
|
+
// port. It will be MACH_PORT_NULL if you call it from the main thread.
|
263
263
|
{
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
}
|
305
|
-
}
|
306
|
-
assert( (err == noErr) == (storage != NULL) );
|
307
|
-
|
308
|
-
// If all went well, copy the port out to our client.
|
309
|
-
|
310
|
-
if (err == noErr) {
|
311
|
-
assert(storage->magic == kPerThreadStorageMagic);
|
312
|
-
assert(storage->port != MACH_PORT_NULL);
|
313
|
-
*replyPortPtr = storage->port;
|
314
|
-
}
|
264
|
+
OSStatus err;
|
265
|
+
|
266
|
+
assert( replyPortPtr != NULL);
|
267
|
+
assert(*replyPortPtr == MACH_PORT_NULL);
|
268
|
+
|
269
|
+
// Initialise ourselves the first time that we're called.
|
270
|
+
|
271
|
+
err = (OSStatus) pthread_once(&sInited, InitRoutine);
|
272
|
+
|
273
|
+
// If something went wrong, return the latched error.
|
274
|
+
|
275
|
+
if ( (err == noErr) && (sPerThreadStorageKeyInitErrNum != noErr) ) {
|
276
|
+
err = sPerThreadStorageKeyInitErrNum;
|
277
|
+
}
|
278
|
+
|
279
|
+
// Now do the real work.
|
280
|
+
|
281
|
+
if (err == noErr) {
|
282
|
+
if ( pthread_main_np() ) {
|
283
|
+
// This is the main thread, so do nothing; leave *replyPortPtr set
|
284
|
+
// to MACH_PORT_NULL.
|
285
|
+
assert(*replyPortPtr == MACH_PORT_NULL);
|
286
|
+
} else {
|
287
|
+
PerThreadStorage * storage;
|
288
|
+
|
289
|
+
// Get the per-thread storage for this thread.
|
290
|
+
|
291
|
+
storage = (PerThreadStorage *) pthread_getspecific(sPerThreadStorageKey);
|
292
|
+
if (storage == NULL) {
|
293
|
+
|
294
|
+
// The per-thread storage hasn't been allocated yet for this specific
|
295
|
+
// thread. Let's go allocate it and attach it to this thread.
|
296
|
+
|
297
|
+
err = AllocatePortFromPool(&storage);
|
298
|
+
if (err == noErr) {
|
299
|
+
err = (OSStatus) pthread_setspecific(sPerThreadStorageKey, (void *) storage);
|
300
|
+
if (err != noErr) {
|
301
|
+
ReturnPortToPool(storage);
|
302
|
+
storage = NULL;
|
303
|
+
}
|
315
304
|
}
|
305
|
+
}
|
306
|
+
assert( (err == noErr) == (storage != NULL) );
|
307
|
+
|
308
|
+
// If all went well, copy the port out to our client.
|
309
|
+
|
310
|
+
if (err == noErr) {
|
311
|
+
assert(storage->magic == kPerThreadStorageMagic);
|
312
|
+
assert(storage->port != MACH_PORT_NULL);
|
313
|
+
*replyPortPtr = storage->port;
|
314
|
+
}
|
316
315
|
}
|
316
|
+
}
|
317
317
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
318
|
+
// no error + MACH_PORT_NULL is a valid response if we're on the main
|
319
|
+
// thread.
|
320
|
+
//
|
321
|
+
// assert( (err == noErr) == (*replyPortPtr != MACH_PORT_NULL) );
|
322
|
+
assert( (*replyPortPtr == MACH_PORT_NULL) || (err == noErr) );
|
323
|
+
|
324
|
+
return err;
|
325
325
|
}
|
326
326
|
|
327
327
|
static void PerThreadStorageDestructor(void *keyValue)
|
328
|
-
|
329
|
-
|
330
|
-
|
328
|
+
// Called by pthreads when a thread dies and it has a non-null value for our
|
329
|
+
// per-thread storage key. We use this callback to return the thread's
|
330
|
+
// Apple event reply port to the pool.
|
331
331
|
{
|
332
|
-
|
332
|
+
PerThreadStorage * storage;
|
333
|
+
|
334
|
+
storage = (PerThreadStorage *) keyValue;
|
335
|
+
assert(storage != NULL); // pthread won't call us if it's NULL
|
336
|
+
assert(storage->magic == kPerThreadStorageMagic);
|
337
|
+
assert(storage->port != MACH_PORT_NULL);
|
338
|
+
|
339
|
+
// Return the port associated with this thread to the pool.
|
333
340
|
|
334
|
-
|
335
|
-
assert(storage != NULL); // pthread won't call us if it's NULL
|
336
|
-
assert(storage->magic == kPerThreadStorageMagic);
|
337
|
-
assert(storage->port != MACH_PORT_NULL);
|
338
|
-
|
339
|
-
// Return the port associated with this thread to the pool.
|
340
|
-
|
341
|
-
ReturnPortToPool(storage);
|
341
|
+
ReturnPortToPool(storage);
|
342
342
|
|
343
|
-
|
344
|
-
|
343
|
+
// pthreads has already set this thread's per-thread storage for our key to
|
344
|
+
// NULL before calling us. So we don't need to do anything to remove it.
|
345
345
|
|
346
|
-
|
346
|
+
assert( pthread_getspecific(sPerThreadStorageKey) == NULL );
|
347
347
|
}
|
348
348
|
|
349
349
|
OSStatus SendMessageThreadSafe(
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
)
|
355
|
-
|
350
|
+
AppleEvent * eventPtr,
|
351
|
+
AppleEvent * replyPtr,
|
352
|
+
AESendMode sendMode,
|
353
|
+
long timeOutInTicks
|
354
|
+
)
|
355
|
+
// See comment in header.
|
356
356
|
{
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
}
|
371
|
-
}
|
372
|
-
|
373
|
-
// Call through to AESendMessage.
|
374
|
-
|
375
|
-
if (err == noErr) {
|
376
|
-
err = AESendMessage(eventPtr, replyPtr, sendMode, timeOutInTicks);
|
357
|
+
OSStatus err = noErr;
|
358
|
+
mach_port_t replyPort;
|
359
|
+
assert(eventPtr != NULL);
|
360
|
+
assert(replyPtr != NULL);
|
361
|
+
|
362
|
+
if (sendMode && kAEWaitReply) {
|
363
|
+
replyPort = MACH_PORT_NULL;
|
364
|
+
|
365
|
+
// Set up the reply port if necessary.
|
366
|
+
|
367
|
+
err = BindReplyMachPortToThread(&replyPort);
|
368
|
+
if ( (err == noErr) && (replyPort != MACH_PORT_NULL) ) {
|
369
|
+
err = AEPutAttributePtr(eventPtr, keyReplyPortAttr, typeMachPort, &replyPort, sizeof(replyPort));
|
377
370
|
}
|
378
|
-
|
379
|
-
|
371
|
+
}
|
372
|
+
|
373
|
+
// Call through to AESendMessage.
|
374
|
+
|
375
|
+
if (err == noErr) {
|
376
|
+
err = AESendMessage(eventPtr, replyPtr, sendMode, timeOutInTicks);
|
377
|
+
}
|
378
|
+
|
379
|
+
return err;
|
380
380
|
}
|