/* * Wait for a thread to finish. * * 'mu_ref' is a lock we should use for the waiting; initially unlocked. * Same lock as passed to THREAD_EXIT. * * Returns TRUE for succesful wait, FALSE for timed out */ bool_t THREAD_WAIT( THREAD_T *ref, double secs , SIGNAL_T *signal_ref, MUTEX_T *mu_ref, volatile enum e_status *st_ref) { struct timespec ts_store; const struct timespec *timeout= NULL; bool_t done; // Do timeout counting before the locks // #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT if (secs>=0.0) #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR if (secs>0.0) #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR { prepare_timeout( &ts_store, now_secs()+secs ); timeout= &ts_store; } #if THREADWAIT_METHOD == THREADWAIT_TIMEOUT /* Thread is joinable */ if (!timeout) { PT_CALL( pthread_join( *ref, NULL /*ignore exit value*/ )); done= TRUE; } else { int rc= PTHREAD_TIMEDJOIN( *ref, NULL, timeout ); if ((rc!=0) && (rc!=ETIMEDOUT)) { _PT_FAIL( rc, "PTHREAD_TIMEDJOIN", __FILE__, __LINE__-2 ); } done= rc==0; } #else // THREADWAIT_METHOD == THREADWAIT_CONDVAR /* Since we've set the thread up as PTHREAD_CREATE_DETACHED, we cannot * join with it. Use the cond.var. */ (void) ref; // unused MUTEX_LOCK( mu_ref ); // 'secs'==0.0 does not need to wait, just take the current status // within the 'mu_ref' locks // if (secs != 0.0) { while( *st_ref < DONE ) { if (!timeout) { PT_CALL( pthread_cond_wait( signal_ref, mu_ref )); } else { int rc= pthread_cond_timedwait( signal_ref, mu_ref, timeout ); if (rc==ETIMEDOUT) break; if (rc!=0) _PT_FAIL( rc, "pthread_cond_timedwait", __FILE__, __LINE__-2 ); } } } done= *st_ref >= DONE; // DONE|ERROR_ST|CANCELLED MUTEX_UNLOCK( mu_ref ); #endif // THREADWAIT_METHOD == THREADWAIT_CONDVAR return done; }
/*! * \brief Handle I2C I/O request. * * \todo There is no bus error checking at all... */ static int usb_i2c_io(struct usb_setup_data *req, uint8_t *buf, uint16_t *len) { uint32_t reg32 __attribute__((unused)); /* Interpret the request */ uint8_t cmd = req->bRequest; uint8_t address = req->wIndex; uint8_t is_read = req->wValue & I2C_M_RD; uint8_t size = req->wLength; i2c_ctx_t ctx; i2c_ctx_init(&ctx, I2C1); /* We can ignore CMD_I2C_BEGIN, the hardware will work out which * type of start condition to generate. */ PT_CALL(&ctx.leaf, i2c_ctx_start(&ctx)); if (ctx.err) goto err; /* Send the address */ PT_CALL(&ctx.leaf, i2c_ctx_sendaddr(&ctx, address, (is_read ? size : 0))); if (ctx.err) goto err; /* Perform the transaction */ for (int i=0; i<size; i++) { PT_CALL(&ctx.leaf, is_read ? i2c_ctx_getdata(&ctx, buf + i) : i2c_ctx_senddata(&ctx, buf[i])); if (ctx.err) goto err; } /* Stop the transaction if requested and this is a write transaction * (reads are stopped automatically) */ if (cmd & CMD_I2C_END && !is_read) { PT_CALL(&ctx.leaf, i2c_ctx_stop(&ctx)); if (ctx.err) goto err; } status = STATUS_ADDRESS_ACK; *len = (is_read ? size : 0); return USBD_REQ_HANDLED; err: i2c_ctx_reset(&ctx); status = STATUS_ADDRESS_NACK; *len = 0; return USBD_REQ_HANDLED; }
PT(ssd1306_printf,uint8_t row, uint8_t col, __code const char * format, ...) { va_list args; PT_B PT_LOCK(busy); va_start(args,format); i=0; printf_tiny(format,args); va_end(args); sbuf[16]=0; //force trancation PT_CALL(ssd1306_move,row,col); i=0; while(sbuf[i] != 0) { s_putc(sbuf[i++]); PT_CALL(ssd1306_tile,buf,9); } PT_UNLOCK(busy); PT_E }
bool_t SIGNAL_WAIT( SIGNAL_T *ref, pthread_mutex_t *mu, time_d abs_secs ) { if (abs_secs<0.0) { PT_CALL( pthread_cond_wait( ref, mu ) ); // infinite } else { int rc; struct timespec ts; assert( abs_secs != 0.0 ); prepare_timeout( &ts, abs_secs ); rc= pthread_cond_timedwait( ref, mu, &ts ); if (rc==ETIMEDOUT) return FALSE; if (rc) { _PT_FAIL( rc, "pthread_cond_timedwait()", __FILE__, __LINE__ ); } } return TRUE; }
void THREAD_CREATE( THREAD_T* ref, THREAD_RETURN_T (*func)( void * ), void *data, int prio /* -2..+2 */ ) { pthread_attr_t _a; pthread_attr_t *a= &_a; struct sched_param sp; PT_CALL( pthread_attr_init(a) ); #ifndef PTHREAD_TIMEDJOIN // We create a NON-JOINABLE thread. This is mainly due to the lack of // 'pthread_timedjoin()', but does offer other benefits (s.a. earlier // freeing of the thread's resources). // //PT_CALL( pthread_attr_setdetachstate(a,PTHREAD_CREATE_DETACHED) ); #endif // Use this to find a system's default stack size (DEBUG) #if 0 { size_t n; pthread_attr_getstacksize( a, &n ); fprintf( stderr, "Getstack: %u\n", (unsigned int)n ); } // 524288 on OS X // 2097152 on Linux x86 (Ubuntu 7.04) // 1048576 on FreeBSD 6.2 SMP i386 #endif #if (defined _THREAD_STACK_SIZE) && (_THREAD_STACK_SIZE > 0) PT_CALL( pthread_attr_setstacksize( a, _THREAD_STACK_SIZE ) ); #endif bool_t normal= #if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR) !sudo; // with sudo, even normal thread must use SCHED_RR #else prio == 0; // create a default thread if #endif if (!normal) { // NB: PThreads priority handling is about as twisty as one can get it // (and then some). DON*T TRUST ANYTHING YOU READ ON THE NET!!! // "The specified scheduling parameters are only used if the scheduling // parameter inheritance attribute is PTHREAD_EXPLICIT_SCHED." // #ifndef ANDROID PT_CALL( pthread_attr_setinheritsched( a, PTHREAD_EXPLICIT_SCHED ) ); #endif //--- // "Select the scheduling policy for the thread: one of SCHED_OTHER // (regular, non-real-time scheduling), SCHED_RR (real-time, // round-robin) or SCHED_FIFO (real-time, first-in first-out)." // // "Using the RR policy ensures that all threads having the same // priority level will be scheduled equally, regardless of their activity." // // "For SCHED_FIFO and SCHED_RR, the only required member of the // sched_param structure is the priority sched_priority. For SCHED_OTHER, // the affected scheduling parameters are implementation-defined." // // "The priority of a thread is specified as a delta which is added to // the priority of the process." // // ".. priority is an integer value, in the range from 1 to 127. // 1 is the least-favored priority, 127 is the most-favored." // // "Priority level 0 cannot be used: it is reserved for the system." // // "When you use specify a priority of -99 in a call to // pthread_setschedparam(), the priority of the target thread is // lowered to the lowest possible value." // // ... // ** CONCLUSION ** // // PThread priorities are _hugely_ system specific, and we need at // least OS specific settings. Hopefully, Linuxes and OS X versions // are uniform enough, among each other... // #ifdef PLATFORM_OSX // AK 10-Apr-07 (OS X PowerPC 10.4.9): // // With SCHED_RR, 26 seems to be the "normal" priority, where setting // it does not seem to affect the order of threads processed. // // With SCHED_OTHER, the range 25..32 is normal (maybe the same 26, // but the difference is not so clear with OTHER). // // 'sched_get_priority_min()' and '..max()' give 15, 47 as the // priority limits. This could imply, user mode applications won't // be able to use values outside of that range. // #define _PRIO_MODE SCHED_OTHER // OS X 10.4.9 (PowerPC) gives ENOTSUP for process scope //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS #define _PRIO_HI 32 // seems to work (_carefully_ picked!) #define _PRIO_0 26 // detected #define _PRIO_LO 1 // seems to work (tested) #elif defined(PLATFORM_LINUX) // (based on Ubuntu Linux 2.6.15 kernel) // // SCHED_OTHER is the default policy, but does not allow for priorities. // SCHED_RR allows priorities, all of which (1..99) are higher than // a thread with SCHED_OTHER policy. // // <http://kerneltrap.org/node/6080> // <http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library> // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> // // Manuals suggest checking #ifdef _POSIX_THREAD_PRIORITY_SCHEDULING, // but even Ubuntu does not seem to define it. // #define _PRIO_MODE SCHED_RR // NTLP 2.5: only system scope allowed (being the basic reason why // root privileges are required..) //#define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS #define _PRIO_HI 99 #define _PRIO_0 50 #define _PRIO_LO 1 #elif defined(PLATFORM_BSD) // // <http://www.net.in.tum.de/~gregor/docs/pthread-scheduling.html> // // "When control over the thread scheduling is desired, then FreeBSD // with the libpthread implementation is by far the best choice .." // #define _PRIO_MODE SCHED_OTHER #define _PRIO_SCOPE PTHREAD_SCOPE_PROCESS #define _PRIO_HI 31 #define _PRIO_0 15 #define _PRIO_LO 1 #elif defined(PLATFORM_CYGWIN) // // TBD: Find right values for Cygwin // #else #error "Unknown OS: not implemented!" #endif #ifdef _PRIO_SCOPE PT_CALL( pthread_attr_setscope( a, _PRIO_SCOPE ) ); #endif PT_CALL( pthread_attr_setschedpolicy( a, _PRIO_MODE ) ); #define _PRIO_AN (_PRIO_0 + ((_PRIO_HI-_PRIO_0)/2) ) #define _PRIO_BN (_PRIO_LO + ((_PRIO_0-_PRIO_LO)/2) ) sp.sched_priority= (prio == +2) ? _PRIO_HI : (prio == +1) ? _PRIO_AN : #if defined(PLATFORM_LINUX) && defined(LINUX_SCHED_RR) (prio == 0) ? _PRIO_0 : #endif (prio == -1) ? _PRIO_BN : _PRIO_LO; PT_CALL( pthread_attr_setschedparam( a, &sp ) ); } //--- // Seems on OS X, _POSIX_THREAD_THREADS_MAX is some kind of system // thread limit (not userland thread). Actual limit for us is way higher. // PTHREAD_THREADS_MAX is not defined (even though man page refers to it!) // # ifndef THREAD_CREATE_RETRIES_MAX // Don't bother with retries; a failure is a failure // { int rc= pthread_create( ref, a, func, data ); if (rc) _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__-1 ); } # else # error "This code deprecated" /* // Wait slightly if thread creation has exchausted the system // { uint_t retries; for( retries=0; retries<THREAD_CREATE_RETRIES_MAX; retries++ ) { int rc= pthread_create( ref, a, func, data ); // // OS X / Linux: // EAGAIN: ".. lacked the necessary resources to create // another thread, or the system-imposed limit on the // total number of threads in a process // [PTHREAD_THREADS_MAX] would be exceeded." // EINVAL: attr is invalid // Linux: // EPERM: no rights for given parameters or scheduling (no sudo) // ENOMEM: (known to fail with this code, too - not listed in man) if (rc==0) break; // ok! // In practise, exhaustion seems to be coming from memory, not a // maximum number of threads. Keep tuning... ;) // if (rc==EAGAIN) { //fprintf( stderr, "Looping (retries=%d) ", retries ); // DEBUG // Try again, later. Yield(); } else { _PT_FAIL( rc, "pthread_create()", __FILE__, __LINE__ ); } } } */ # endif if (a) { PT_CALL( pthread_attr_destroy(a) ); } }
void SIGNAL_ALL( SIGNAL_T *ref ) { PT_CALL( pthread_cond_broadcast(ref) ); // wake up ALL waiting threads }
void SIGNAL_ONE( SIGNAL_T *ref ) { PT_CALL( pthread_cond_signal(ref) ); // wake up ONE (or no) waiting thread }
void SIGNAL_FREE( SIGNAL_T *ref ) { PT_CALL( pthread_cond_destroy(ref) ); }
void SIGNAL_INIT( SIGNAL_T *ref ) { PT_CALL( pthread_cond_init(ref,NULL /*attr*/) ); }