| 1 | #include "semaphore.hfa"
 | 
|---|
| 2 | #include "kernel_private.hfa"
 | 
|---|
| 3 | #include <stdlib.h>
 | 
|---|
| 4 | #include <stdio.h>
 | 
|---|
| 5 | 
 | 
|---|
| 6 | #include <kernel.hfa>
 | 
|---|
| 7 | #include <stdlib.hfa>
 | 
|---|
| 8 | #include <thread.hfa>
 | 
|---|
| 9 | 
 | 
|---|
| 10 | forall(dtype L | is_blocking_lock(L)) {
 | 
|---|
| 11 | 
 | 
|---|
| 12 |         void ?{}(base_semaphore(L) & this, int count, bool is_binary) {
 | 
|---|
| 13 |                 this.count = count;
 | 
|---|
| 14 |                 this.lock{};
 | 
|---|
| 15 |                 this.blocked_threads{};
 | 
|---|
| 16 |                 this.is_binary = is_binary;
 | 
|---|
| 17 |         }
 | 
|---|
| 18 | 
 | 
|---|
| 19 |         void ^?{}(base_semaphore(L) & this) {
 | 
|---|
| 20 |                 // default
 | 
|---|
| 21 |         }
 | 
|---|
| 22 | 
 | 
|---|
| 23 |         void ?{}(binary_semaphore(L) & this) {
 | 
|---|
| 24 |                 ((base_semaphore(L) &)this){ 1, true };
 | 
|---|
| 25 |         }
 | 
|---|
| 26 | 
 | 
|---|
| 27 |         void ?{}(binary_semaphore(L) & this, int count) {
 | 
|---|
| 28 |                 ((base_semaphore(L) &)this){ count, true };
 | 
|---|
| 29 |         }
 | 
|---|
| 30 | 
 | 
|---|
| 31 |         void ^?{}(binary_semaphore(L) & this) {
 | 
|---|
| 32 |                 // default
 | 
|---|
| 33 |         }
 | 
|---|
| 34 | 
 | 
|---|
| 35 |         void ?{}(counting_semaphore(L) & this) {
 | 
|---|
| 36 |                 ((base_semaphore(L) &)this){ 1, false };
 | 
|---|
| 37 |         }
 | 
|---|
| 38 | 
 | 
|---|
| 39 |         void ?{}(counting_semaphore(L) & this, int count) {
 | 
|---|
| 40 |                 ((base_semaphore(L) &)this){ count, false };
 | 
|---|
| 41 |         }
 | 
|---|
| 42 | 
 | 
|---|
| 43 |         void ^?{}(counting_semaphore(L) & this) {
 | 
|---|
| 44 |                 // default
 | 
|---|
| 45 |         }
 | 
|---|
| 46 | 
 | 
|---|
| 47 |         void ?{}( alarm_node_semaphore(L) & this, $thread * thrd, Time alarm, Duration period, Alarm_Callback callback ) {
 | 
|---|
| 48 |                 this.alarm_node{ thrd, alarm, period, callback };
 | 
|---|
| 49 |         }
 | 
|---|
| 50 | 
 | 
|---|
| 51 |         void ^?{}( alarm_node_semaphore(L) & this ) {
 | 
|---|
| 52 | 
 | 
|---|
| 53 |         }
 | 
|---|
| 54 | 
 | 
|---|
| 55 |         void add_( base_semaphore(L) & this, struct $thread * t ) with( this ) {
 | 
|---|
| 56 |                 lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 57 |                 #if !defined( __CFA_NO_STATISTICS__ )
 | 
|---|
| 58 |                         kernelTLS.this_stats = t->curr_cluster->stats;
 | 
|---|
| 59 |                 #endif
 | 
|---|
| 60 |                 unpark( t );
 | 
|---|
| 61 |                 unlock( lock );
 | 
|---|
| 62 |         }
 | 
|---|
| 63 | 
 | 
|---|
| 64 |         void remove_( base_semaphore(L) & this ) with( this ) {
 | 
|---|
| 65 |                 V(this);
 | 
|---|
| 66 |         }
 | 
|---|
| 67 | 
 | 
|---|
| 68 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 69 |         // These extras are needed since the inheritance is broken with traits
 | 
|---|
| 70 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 71 | 
 | 
|---|
| 72 |         void add_( binary_semaphore(L) & this, struct $thread * t ) with( this ) {
 | 
|---|
| 73 |                 add_( (base_semaphore(L) &)this, t );
 | 
|---|
| 74 |         }
 | 
|---|
| 75 | 
 | 
|---|
| 76 |         void remove_( binary_semaphore(L) & this ) with( this ) {
 | 
|---|
| 77 |                 remove_( (base_semaphore(L) &)this );
 | 
|---|
| 78 |         }
 | 
|---|
| 79 | 
 | 
|---|
| 80 |         void add_( counting_semaphore(L) & this, struct $thread * t ) with( this ) {
 | 
|---|
| 81 |                 add_( (base_semaphore(L) &)this, t );
 | 
|---|
| 82 |         }
 | 
|---|
| 83 | 
 | 
|---|
| 84 |         void remove_( counting_semaphore(L) & this ) with( this ) {
 | 
|---|
| 85 |                 remove_( (base_semaphore(L) &)this );
 | 
|---|
| 86 |         }
 | 
|---|
| 87 | 
 | 
|---|
| 88 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 89 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 90 | 
 | 
|---|
| 91 |         void timeout_handler ( alarm_node_semaphore(L) & this ) with( this ) {
 | 
|---|
| 92 |         // This condition_variable member is called from the kernel, and therefore, cannot block, but it can spin.
 | 
|---|
| 93 |             lock( sem->lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 94 |             if ( (*i)->listed ) {                       // is thread on queue
 | 
|---|
| 95 |                 info_thread(L) * copy = *i;
 | 
|---|
| 96 |                         remove( sem->blocked_threads, i );               //remove this thread O(1)
 | 
|---|
| 97 |                         sem->count++;
 | 
|---|
| 98 |                         if( !copy->lock ) {
 | 
|---|
| 99 |                                 unlock( sem->lock );
 | 
|---|
| 100 |                                 #if !defined( __CFA_NO_STATISTICS__ )
 | 
|---|
| 101 |                                         kernelTLS.this_stats = copy->t->curr_cluster->stats;
 | 
|---|
| 102 |                                 #endif
 | 
|---|
| 103 |                                 unpark( copy->t );
 | 
|---|
| 104 |                 } else {
 | 
|---|
| 105 |                         add_(*copy->lock, copy->t);                     // call lock's add_
 | 
|---|
| 106 |                 }
 | 
|---|
| 107 |             }
 | 
|---|
| 108 |             unlock( sem->lock );
 | 
|---|
| 109 |         }
 | 
|---|
| 110 | 
 | 
|---|
| 111 |         void alarm_node_sem_cast( alarm_node_t & a ) {
 | 
|---|
| 112 |                 timeout_handler( (alarm_node_semaphore(L) &)a );
 | 
|---|
| 113 |         }
 | 
|---|
| 114 | 
 | 
|---|
| 115 |         void handle_P(base_semaphore(L) & this, info_thread( L ) & i ) with( this ) {
 | 
|---|
| 116 |                 lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 117 |                 if ( count <= 0) {
 | 
|---|
| 118 |                         append( blocked_threads, &i );
 | 
|---|
| 119 | 
 | 
|---|
| 120 |                         if (!is_binary) count--;
 | 
|---|
| 121 | 
 | 
|---|
| 122 |                         i.listed = true;
 | 
|---|
| 123 | 
 | 
|---|
| 124 |                         size_t recursion_count;
 | 
|---|
| 125 |                         if (i.lock) {
 | 
|---|
| 126 |                                 recursion_count = get_recursion_count( *i.lock );
 | 
|---|
| 127 |                                 remove_( *i.lock );
 | 
|---|
| 128 |                         }
 | 
|---|
| 129 | 
 | 
|---|
| 130 |                         unlock( lock );
 | 
|---|
| 131 |                         park( );
 | 
|---|
| 132 | 
 | 
|---|
| 133 |                         if (i.lock) set_recursion_count( *i.lock, recursion_count );
 | 
|---|
| 134 |                 } else {
 | 
|---|
| 135 |                         if (i.lock) {
 | 
|---|
| 136 |                                 size_t recursion_count = get_recursion_count( *i.lock );
 | 
|---|
| 137 |                                 remove_( *i.lock );
 | 
|---|
| 138 |                                 add_( *i.lock, i.t );
 | 
|---|
| 139 |                                 set_recursion_count( *i.lock, recursion_count );
 | 
|---|
| 140 |                         }
 | 
|---|
| 141 |                         count--;
 | 
|---|
| 142 |                         unlock( lock );
 | 
|---|
| 143 |                 }
 | 
|---|
| 144 |         }
 | 
|---|
| 145 | 
 | 
|---|
| 146 |         void handle_P_timeout(base_semaphore(L) & this, info_thread( L ) & i, Time time) with( this ) {
 | 
|---|
| 147 |                 lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 148 |                 if ( count <= 0) {
 | 
|---|
| 149 |                         append( blocked_threads, &i );
 | 
|---|
| 150 | 
 | 
|---|
| 151 |                         if (!is_binary) count--;
 | 
|---|
| 152 | 
 | 
|---|
| 153 |                         info_thread(L) * queue_ptr = &i;
 | 
|---|
| 154 |                         i.listed = true;
 | 
|---|
| 155 | 
 | 
|---|
| 156 |                         alarm_node_semaphore(L) node_wrap = { i.t, time, 0`s, alarm_node_sem_cast };
 | 
|---|
| 157 |                         node_wrap.sem = &this;
 | 
|---|
| 158 |                         node_wrap.i = &queue_ptr;
 | 
|---|
| 159 | 
 | 
|---|
| 160 |                         register_self( &node_wrap.alarm_node );
 | 
|---|
| 161 | 
 | 
|---|
| 162 |                         size_t recursion_count;
 | 
|---|
| 163 |                         if (i.lock) {
 | 
|---|
| 164 |                                 recursion_count = get_recursion_count( *i.lock );
 | 
|---|
| 165 |                                 remove_( *i.lock );
 | 
|---|
| 166 |                         }
 | 
|---|
| 167 | 
 | 
|---|
| 168 |                         unlock( lock );
 | 
|---|
| 169 |                         park( );
 | 
|---|
| 170 | 
 | 
|---|
| 171 |                         if (i.lock) set_recursion_count( *i.lock, recursion_count );
 | 
|---|
| 172 |                 } else {
 | 
|---|
| 173 |                         count--;
 | 
|---|
| 174 |                         unlock( lock );
 | 
|---|
| 175 |                 }
 | 
|---|
| 176 |         }
 | 
|---|
| 177 | 
 | 
|---|
| 178 |         void P(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 179 |                 info_thread( L ) i = { kernelTLS.this_thread };
 | 
|---|
| 180 |                 handle_P(this, i);
 | 
|---|
| 181 |         }
 | 
|---|
| 182 | 
 | 
|---|
| 183 |         void P(base_semaphore(L) & this, uintptr_t info) with( this ) {
 | 
|---|
| 184 |                 info_thread( L ) i = { kernelTLS.this_thread, info };
 | 
|---|
| 185 |                 handle_P(this, i);
 | 
|---|
| 186 |         }
 | 
|---|
| 187 | 
 | 
|---|
| 188 |         void P(base_semaphore(L) & this, Duration duration) with( this ) {
 | 
|---|
| 189 |                 info_thread( L ) i = { kernelTLS.this_thread };
 | 
|---|
| 190 |                 handle_P_timeout(this, i, __kernel_get_time() + duration);
 | 
|---|
| 191 |         }
 | 
|---|
| 192 | 
 | 
|---|
| 193 |         void P(base_semaphore(L) & this, uintptr_t info, Duration duration) with( this ) {
 | 
|---|
| 194 |                 info_thread( L ) i = { kernelTLS.this_thread, info };
 | 
|---|
| 195 |                 handle_P_timeout(this, i, __kernel_get_time() + duration);
 | 
|---|
| 196 |         }
 | 
|---|
| 197 | 
 | 
|---|
| 198 |         void P(base_semaphore(L) & this, Time time) with( this ) {
 | 
|---|
| 199 |                 info_thread( L ) i = { kernelTLS.this_thread };
 | 
|---|
| 200 |                 handle_P_timeout(this, i, time);
 | 
|---|
| 201 |         }
 | 
|---|
| 202 | 
 | 
|---|
| 203 |         void P(base_semaphore(L) & this, uintptr_t info, Time time) with( this ) {
 | 
|---|
| 204 |                 info_thread( L ) i = { kernelTLS.this_thread, info };
 | 
|---|
| 205 |                 handle_P_timeout(this, i, time);
 | 
|---|
| 206 |         }
 | 
|---|
| 207 | 
 | 
|---|
| 208 |         void P(base_semaphore(L) & this, L & l ) with( this ) {
 | 
|---|
| 209 |                 info_thread( L ) i = { kernelTLS.this_thread };
 | 
|---|
| 210 |                 i.lock = &l;
 | 
|---|
| 211 |                 handle_P(this, i);
 | 
|---|
| 212 |         }
 | 
|---|
| 213 | 
 | 
|---|
| 214 |         void P(base_semaphore(L) & this, L & l, uintptr_t info) with( this ) {
 | 
|---|
| 215 |                 info_thread( L ) i = { kernelTLS.this_thread, info };
 | 
|---|
| 216 |                 i.lock = &l;
 | 
|---|
| 217 |                 handle_P(this, i);
 | 
|---|
| 218 |         }
 | 
|---|
| 219 | 
 | 
|---|
| 220 |         void P(base_semaphore(L) & this, L & l, Duration duration ) with( this ) {
 | 
|---|
| 221 |                 info_thread( L ) i = { kernelTLS.this_thread };
 | 
|---|
| 222 |                 i.lock = &l;
 | 
|---|
| 223 |                 handle_P_timeout(this, i, __kernel_get_time() + duration);
 | 
|---|
| 224 |         }
 | 
|---|
| 225 | 
 | 
|---|
| 226 |         void P(base_semaphore(L) & this, L & l, uintptr_t info, Duration duration) with( this ) {
 | 
|---|
| 227 |                 info_thread( L ) i = { kernelTLS.this_thread, info };
 | 
|---|
| 228 |                 i.lock = &l;
 | 
|---|
| 229 |                 handle_P_timeout(this, i, __kernel_get_time() + duration);
 | 
|---|
| 230 |         }
 | 
|---|
| 231 | 
 | 
|---|
| 232 |         void P(base_semaphore(L) & this, L & l, Time time) with( this ) {
 | 
|---|
| 233 |                 info_thread( L ) i = { kernelTLS.this_thread };
 | 
|---|
| 234 |                 i.lock = &l;
 | 
|---|
| 235 |                 handle_P_timeout(this, i, time);
 | 
|---|
| 236 |         }
 | 
|---|
| 237 | 
 | 
|---|
| 238 |         void P(base_semaphore(L) & this, L & l, uintptr_t info, Time time) with( this ) {
 | 
|---|
| 239 |                 info_thread( L ) i = { kernelTLS.this_thread, info };
 | 
|---|
| 240 |                 i.lock = &l;
 | 
|---|
| 241 |                 handle_P_timeout(this, i, time);
 | 
|---|
| 242 |         }
 | 
|---|
| 243 | 
 | 
|---|
| 244 |         bool tryP(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 245 |                 lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 246 |                 if ( count <= 0) {
 | 
|---|
| 247 |                         unlock( lock );
 | 
|---|
| 248 |                         return false;
 | 
|---|
| 249 |                 } else {
 | 
|---|
| 250 |                         count--;
 | 
|---|
| 251 |                 }
 | 
|---|
| 252 |                 unlock( lock );
 | 
|---|
| 253 |         }
 | 
|---|
| 254 | 
 | 
|---|
| 255 |         void V(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 256 |                 lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 257 |                 if( count < 0) {
 | 
|---|
| 258 |                         info_thread(L) * i = pop_head( blocked_threads );
 | 
|---|
| 259 |                         i->listed = false;
 | 
|---|
| 260 |                         count++;
 | 
|---|
| 261 |                         if ( i != 0p ) {
 | 
|---|
| 262 |                                 if (i->lock) {
 | 
|---|
| 263 |                                         add_(*i->lock, i->t);
 | 
|---|
| 264 |                                 } else {
 | 
|---|
| 265 |                                         unpark(i->t);
 | 
|---|
| 266 |                                 }
 | 
|---|
| 267 |                         }
 | 
|---|
| 268 |                 } else if (!is_binary || count == 0) {
 | 
|---|
| 269 |                         count++;
 | 
|---|
| 270 |                 } else {
 | 
|---|
| 271 |                         fprintf( stderr, "A binary semaphore was V'd when it was already at 1" );
 | 
|---|
| 272 |                 }
 | 
|---|
| 273 |                 unlock( lock );
 | 
|---|
| 274 |         }
 | 
|---|
| 275 | 
 | 
|---|
| 276 | 
 | 
|---|
| 277 |         // TODO: Should we be able to V a binary semaphore multiple times to wake up multiple thds?
 | 
|---|
| 278 |         // right now we can't
 | 
|---|
| 279 |         void V(base_semaphore(L) & this, int times) with( this ) {
 | 
|---|
| 280 |                 assert( times > 0 );
 | 
|---|
| 281 |                 lock( lock __cfaabi_dbg_ctx2 );
 | 
|---|
| 282 |                 while ( count < 0 && times > 0 ) {
 | 
|---|
| 283 |                         info_thread(L) * i = pop_head( blocked_threads );
 | 
|---|
| 284 |                         i->listed = false;
 | 
|---|
| 285 |                         count++;
 | 
|---|
| 286 |                         if ( i != 0p ) {
 | 
|---|
| 287 |                                 if (i->lock) {
 | 
|---|
| 288 |                                         add_(*i->lock, i->t);
 | 
|---|
| 289 |                                 } else {
 | 
|---|
| 290 |                                         unpark(i->t);
 | 
|---|
| 291 |                                 }
 | 
|---|
| 292 |                         }
 | 
|---|
| 293 |                         times--;
 | 
|---|
| 294 |                 }
 | 
|---|
| 295 |                 if(     !is_binary ) {
 | 
|---|
| 296 |                         count += times;
 | 
|---|
| 297 |                 } else if (count == 0 && times > 1) {
 | 
|---|
| 298 |                         fprintf( stderr, "A binary semaphore was V'd when it was already at 1" );
 | 
|---|
| 299 |                 } else {
 | 
|---|
| 300 |                         count++;
 | 
|---|
| 301 |                 }
 | 
|---|
| 302 |                 unlock( lock );
 | 
|---|
| 303 |         }
 | 
|---|
| 304 | 
 | 
|---|
| 305 |         // void ?++(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 306 |         //      V(this);
 | 
|---|
| 307 |         // }
 | 
|---|
| 308 | 
 | 
|---|
| 309 |         // void ?--(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 310 |         //      P(this);
 | 
|---|
| 311 |         // }
 | 
|---|
| 312 | 
 | 
|---|
| 313 |         // void ?`V(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 314 |         //      V(this);
 | 
|---|
| 315 |         // }
 | 
|---|
| 316 | 
 | 
|---|
| 317 |         // void ?`P(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 318 |         //      P(this);
 | 
|---|
| 319 |         // }
 | 
|---|
| 320 | 
 | 
|---|
| 321 |         uintptr_t front(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 322 |                 info_thread(L) *front = peek(blocked_threads);
 | 
|---|
| 323 |                 if(!blocked_threads) return 0;
 | 
|---|
| 324 |                 return front->info;
 | 
|---|
| 325 |         }
 | 
|---|
| 326 | 
 | 
|---|
| 327 |         bool empty(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 328 |                 return blocked_threads ? false : true;
 | 
|---|
| 329 |         }
 | 
|---|
| 330 | 
 | 
|---|
| 331 |         int counter(base_semaphore(L) & this) with( this ) {
 | 
|---|
| 332 |                 return count;
 | 
|---|
| 333 |         }
 | 
|---|
| 334 | 
 | 
|---|
| 335 |         // these are just to allow the semaphore to be a part of the is_blocking_lock trait
 | 
|---|
| 336 |         // they do nothing
 | 
|---|
| 337 |         void set_recursion_count( base_semaphore(L) & this, size_t recursion ) with( this ) {
 | 
|---|
| 338 |                 // default
 | 
|---|
| 339 |         }
 | 
|---|
| 340 | 
 | 
|---|
| 341 |         size_t get_recursion_count( base_semaphore(L) & this ) with( this ) {
 | 
|---|
| 342 |                 return 0;
 | 
|---|
| 343 |         }
 | 
|---|
| 344 | 
 | 
|---|
| 345 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 346 |         // These extras are needed since the inheritance is broken with traits
 | 
|---|
| 347 |         // normally I'd cast to a semaphore & to call the parent but theres no need
 | 
|---|
| 348 |         // since these routines are so simple
 | 
|---|
| 349 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 350 | 
 | 
|---|
| 351 |         void set_recursion_count( binary_semaphore(L) & this, size_t recursion ) with( this ) {
 | 
|---|
| 352 |                 // default
 | 
|---|
| 353 |         }
 | 
|---|
| 354 | 
 | 
|---|
| 355 |         size_t get_recursion_count( binary_semaphore(L) & this ) with( this ) {
 | 
|---|
| 356 |                 return 0;
 | 
|---|
| 357 |         }
 | 
|---|
| 358 | 
 | 
|---|
| 359 |         void set_recursion_count( counting_semaphore(L) & this, size_t recursion ) with( this ) {
 | 
|---|
| 360 |                 // default
 | 
|---|
| 361 |         }
 | 
|---|
| 362 | 
 | 
|---|
| 363 |         size_t get_recursion_count( counting_semaphore(L) & this ) with( this ) {
 | 
|---|
| 364 |                 return 0;
 | 
|---|
| 365 |         }
 | 
|---|
| 366 | 
 | 
|---|
| 367 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 368 |         ////////////////////////////////////////////////////////////////////////////////
 | 
|---|
| 369 | }
 | 
|---|
| 370 | 
 | 
|---|
| 371 | thread T1 {};
 | 
|---|
| 372 | thread T2 {};
 | 
|---|
| 373 | 
 | 
|---|
| 374 | // counting_semaphore( ) s0, s1;
 | 
|---|
| 375 | 
 | 
|---|
| 376 | // void main( T1 & this ) {
 | 
|---|
| 377 | //      printf("T1 start\n");
 | 
|---|
| 378 | //      V(s1);
 | 
|---|
| 379 | //      P(s0);
 | 
|---|
| 380 | //      P(s0);
 | 
|---|
| 381 | //      V(s1, 2);
 | 
|---|
| 382 |         
 | 
|---|
| 383 | //      printf("T1 done\n");
 | 
|---|
| 384 | // }
 | 
|---|
| 385 | 
 | 
|---|
| 386 | // void main( T2 & this ) {
 | 
|---|
| 387 | //      printf("T2 start\n");
 | 
|---|
| 388 | //      V(s0);
 | 
|---|
| 389 | //      P(s1);
 | 
|---|
| 390 | //      P(s1, s0);
 | 
|---|
| 391 | //      P(s1);
 | 
|---|
| 392 | //      printf("T2 done\n");
 | 
|---|
| 393 | // }
 | 
|---|
| 394 | 
 | 
|---|
| 395 | int main() {
 | 
|---|
| 396 |         printf("start\n");
 | 
|---|
| 397 |         // processor p[2];
 | 
|---|
| 398 |         // {
 | 
|---|
| 399 |         //      T1 t1;
 | 
|---|
| 400 |         //      T2 t2;
 | 
|---|
| 401 |         // }
 | 
|---|
| 402 |         printf("done\n");
 | 
|---|
| 403 | }
 | 
|---|