Warning! We have ended support for Kiln Harmony. We will continue to support Mercurial and Git repositories individually, but not both at the same time.

Please convert this repository to either a Mercurial or Git repository. To do so, have an Admin go to Settings, then click "Convert Repository to Git-only or Mercurial-only", and follow the instructions there.

Alternatively, you can see a list of all Harmony repos, and convert many at once here.

If you have any questions or concerns, please contact us at customer-service@fogcreek.com.

Mercurial
Repositories » pharo-vm Read More
Clone URL:  
Pushed to one repository · View In Graph Contained in tip and develop

reactivated igor's external semaphore changes

Changeset b8a669f711d2

Parent 2c56c4321994

by Profile picture of User 2Camillo Bruni <camillobruni@gmail.com>

Changes to one file · Browse files at b8a669f711d2 Showing diff from parent 2c56c4321994 Diff from another changeset...

 
3
4
5
6
 
7
8
9
 
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
 
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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
176
177
178
179
180
 
 
181
 
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
 
 
 
 
 
 
 
228
229
230
 
231
232
233
234
235
236
237
238
239
240
241
242
 
243
244
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
4
5
 
6
7
8
9
 
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
 
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
175
176
177
178
179
180
181
182
 
 
183
184
185
186
187
188
189
 
 
190
191
192
193
194
195
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
198
199
200
201
 
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
 
 
 
 
 
220
221
222
223
224
225
226
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
 
 
 
 
 
 
 
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
@@ -3,7 +3,7 @@
  *   * Authors: Eliot Miranda & Brad Fowlow   * - * Copyright (c) 2013 3D Immersive Collaboration Consulting, LLC. + * Copyright (C) 2009 by Teleplace, Inc.   *   * All rights reserved.   * @@ -60,31 +60,56 @@
 extern void forceInterruptCheck(void);  extern sqInt doSignalSemaphoreWithIndex(sqInt semaIndex);   -typedef struct { -#if !defined(TARGET_OS_IS_IPHONE) && (x86 || x86-64) /* etc etc x86 allows atomic update of 16 bit values */ - short requests; - short responses; +static volatile int sigIndexLow = 0; +static volatile int sigIndexHigh = 0; +static volatile int sigLock = 0; + + +/* do we need to dynamically grow the queue? */ +#define SQ_DYNAMIC_QUEUE_SIZE 0 + +/* a hardcoded limit for static-sized queue */ +#define SQ_SIGNAL_QUEUE_SIZE 512 + +#if SQ_DYNAMIC_QUEUE_SIZE + static int maxPendingSignals = 0; + static volatile sqInt * signalQueue = 0;  #else - long requests; - long responses; + #define maxPendingSignals SQ_SIGNAL_QUEUE_SIZE + static sqInt signalQueue [maxPendingSignals];  #endif - } SignalRequest;   -static SignalRequest *signalRequests = 0; -static int numSignalRequests = 0; -static volatile sqInt checkSignalRequests; +static inline int hasPendingSignals(void) { + return sigIndexLow != sigIndexHigh; +}   -/* The tides define the minimum range of indices into signalRequests that the - * VM needs to scan. With potentially thousands of indices to scan this can - * save significant lengths of time. - */ -static volatile int tideLock = 0; -static volatile int useTideA = 1; -static volatile sqInt lowTideA = (unsigned long)-1 >> 1, highTideA = -1; -static volatile sqInt lowTideB = (unsigned long)-1 >> 1, highTideB = -1; +static inline int maxQueueSize(void) { + return maxPendingSignals; +}   -int -ioGetMaxExtSemTableSize(void) { return numSignalRequests; } +/* return a successive index in queue, wrap around in round-robin style */ +static inline int succIndex(int index) { + return ((index + 1) == maxQueueSize()) ? 0 : index + 1; +} + + + + +static inline void lockSignalQueue() +{ + volatile int old; + /* spin to obtain a lock */ + + do { + sqLowLevelMFence(); + sqCompareAndSwapRes(sigLock, 0, 1, old ); + } while (old != 0); + +} + +static inline void unlockSignalQueue() { + sigLock = 0; +}    /* Setting this at any time other than start-up can potentially lose requests.   * i.e. during the realloc new storage is allocated, the old contents are copied @@ -94,152 +119,144 @@
  * there is little point. The intended use is to set the table to some adequate   * maximum at start-up and avoid locking altogether.   */ -void -ioSetMaxExtSemTableSize(int n) -{ -#if COGMTVM - /* initialization is a little different in MT. Hack around assert for now */ - if (getVMOSThread()) -#endif - if (numSignalRequests) - assert(ioOSThreadsEqual(ioCurrentOSThread(),getVMOSThread())); - if (numSignalRequests < n) { + +void ioGrowSignalQueue( int n ) { +#if SQ_DYNAMIC_QUEUE_SIZE // ignore, if queue size is static + + /* only to grow */ + if (maxPendingSignals < n) {   extern sqInt highBit(sqInt);   int sz = 1 << highBit(n-1);   assert(sz >= n); - signalRequests = realloc(signalRequests, sz * sizeof(SignalRequest)); - memset(signalRequests + numSignalRequests, - 0, - (sz - numSignalRequests) * sizeof(SignalRequest)); - numSignalRequests = sz; + + + sqInt * newBuf = realloc(signalQueue, sz * sizeof(sqInt)); + + /* we should lock queue when growing, so nobody can access the queue buffer when we mutate the pointer */ + lockSignalQueue(); + signalQueue = newBuf; + unlockSignalQueue(); + + maxPendingSignals = sz;   } +#endif  }   +  void  ioInitExternalSemaphores(void)  { - ioSetMaxExtSemTableSize(INITIAL_EXT_SEM_TABLE_SIZE); + ioGrowSignalQueue(SQ_SIGNAL_QUEUE_SIZE);  }    /* Signal the external semaphore with the given index. Answer non-zero on   * success, zero otherwise. This function is (should be) thread-safe;   * multiple threads may attempt to signal the same semaphore without error.   * An index of zero should be and is silently ignored. + * + * (sig) As well as negative index.   */  sqInt  signalSemaphoreWithIndex(sqInt index)  { - int i = index - 1; - int v; + int next; + + /* An index of zero should be and is silently ignored. */ + if (index <=0) + return 0; + + /* we must use the locking semantics to avoid ABA problem on writing a semaphore index to queue, + so there is no chance for fetching thread to observe queue in inconsistent state. + */ + lockSignalQueue(); + + /* check for queue overflow */ + next = succIndex(sigIndexHigh); + if (next == sigIndexLow ) { + +#if SQ_DYNAMIC_QUEUE_SIZE + // grow and retry + unlockSignalQueue(); + ioGrowSignalQueue( maxPendingSignals + 100); + return signalSemaphoreWithIndex(index);   - /* An index of zero should be and is silently ignored. */ - assert(index >= 0 && index <= numSignalRequests); +#else + unlockSignalQueue(); + // error if queue size is static (perhaps better would be to sleep for a while and retry?) + error("External semaphore signal queue overflow"); +#endif + }   - if ((unsigned)i >= numSignalRequests) - return 0; + signalQueue[sigIndexHigh] = index; + /* make sure semaphore index is written before we advance sigIndexHigh */ + sqLowLevelMFence(); + + sigIndexHigh = next; + /* reset lock */   - sqLowLevelMFence(); - sqAtomicAddConst(signalRequests[i].requests,1); - if (useTideA) { - /* atomic if (lowTideA > i) lowTideA = i; */ - while ((v = lowTideA) > i) { - sqLowLevelMFence(); - sqCompareAndSwap(lowTideA, v, i); - } - /* atomic if (highTideA < i) highTideA = i; */ - while ((v = highTideA) < i) { - sqLowLevelMFence(); - sqCompareAndSwap(highTideA, v, i); - } - } - else { - /* atomic if (lowTideB > i) lowTideB = i; */ - while ((v = lowTideB) > i) { - sqLowLevelMFence(); - sqCompareAndSwap(lowTideB, v, i); - } - /* atomic if (highTideB < i) highTideB = i; */ - while ((v = highTideB) < i) { - sqLowLevelMFence(); - sqCompareAndSwap(highTideB, v, i); - } - } - - checkSignalRequests = 1; - - forceInterruptCheck(); + unlockSignalQueue(); + forceInterruptCheck();   return 1;  }   -/* Signal any external semaphores for which signal requests exist. + +static inline sqInt fetchQueueItem() +{ + // if queue size is dynamic, we should lock while accessing signalQueue + #if SQ_DYNAMIC_QUEUE_SIZE + lockSignalQueue(); + #endif + sqInt result = signalQueue[sigIndexLow]; + #if SQ_DYNAMIC_QUEUE_SIZE + unlockSignalQueue(); + #endif + return result; +} + + + +/* Signal any external semaphores for which signal request(s) are pending in queue.   * Answer whether a context switch occurred. - * Note we no longer ensure the lock table has at least minTableSize elements. - * Instead its size is settable at startup from a value in the image header via - * ioSetMaxExtSemTableSize. This avoids locks on the table while it grows - * (although a lock-free implementation is possible, if tricky). So for the - * moment externalSemaphoreTableSize is not used. + * sigIndexHigh may advance during processing, which is not big deal, + * since we flushing the queue anyways.   */ +  sqInt  doSignalExternalSemaphores(int externalSemaphoreTableSize)  { - int i, switched; - - if (!checkSignalRequests) - return 0; - - switched = 0; - checkSignalRequests = 0; - - if (useTideA) { - useTideA = 0; - sqLowLevelMFence(); - /* doing this here saves a bounds check in doSignalSemaphoreWithIndex */ - if (highTideA >= externalSemaphoreTableSize) - highTideA = externalSemaphoreTableSize - 1; - for (i = lowTideA; i <= highTideA; i++) - while (signalRequests[i].responses != signalRequests[i].requests) { - if (doSignalSemaphoreWithIndex(i+1)) - switched = 1; - ++signalRequests[i].responses; - } - lowTideA = (unsigned long)-1 >> 1, highTideA = -1; - } - else { - useTideA = 1; - sqLowLevelMFence(); - /* doing this here saves a bounds check in doSignalSemaphoreWithIndex */ - if (highTideB >= externalSemaphoreTableSize) - highTideB = externalSemaphoreTableSize - 1; - for (i = lowTideB; i <= highTideB; i++) - while (signalRequests[i].responses != signalRequests[i].requests) { - if (doSignalSemaphoreWithIndex(i+1)) - switched = 1; - ++signalRequests[i].responses; - } - lowTideB = (unsigned long)-1 >> 1, highTideB = -1; - } - - /* If a signal came in while processing, check for signals again soon. - */ - sqLowLevelMFence(); - if (checkSignalRequests) - forceInterruptCheck(); - + int switched = 0; + + while (hasPendingSignals()) { + if(doSignalSemaphoreWithIndex(fetchQueueItem())) + switched = 1; + sigIndexLow = succIndex(sigIndexLow); + }   return switched;  }   +  #if FOR_SQUEAK_VM_TESTS  /* see e.g. tests/sqExternalSemaphores/unixmain.c */  int  allRequestsAreAnswered(int externalSemaphoreTableSize)  { - long i; - for (i = 1; i < externalSemaphoreTableSize; i++) - if (signalRequests[i].responses != signalRequests[i].requests) { - printf("signalRequests[%ld] requests %d responses %d\n", - i, signalRequests[i].requests, signalRequests[i].responses); - return 0; - } + /* yes, it is, otherwise we will get a queue overflow error ;) */   return 1;  }  #endif /* FOR_SQUEAK_VM_TESTS */ + + + +/* Following two are left for compatibility with language side */ + +void ioSetMaxExtSemTableSize(int n) { + /* just ignore */ +} + +int ioGetMaxExtSemTableSize(void) { + // answer an arbitrary large number.. to not confuse image side about it + return 10000000; + /* return maxQueueSize(); */ +} + +