/* * Dynamically initialize the "suspend package" when first used * (called by pthread_once). */ void suspend_init_routine (void) { int status; struct sigaction sigusr1, sigusr2; /* * Initialize a semaphore, to be used by the signal handler to * confirm suspension. We only need one, because suspend & resume * are fully serialized by a mutex. */ status = sem_init (&sem, 0, 1); if (status == -1) errno_abort ("Initializing semaphore"); /* * Allocate the suspended threads array. This array is used to guarantee * idempotency. */ bottom = 10; array = (Victim_t*) calloc (bottom, sizeof (Victim_t)); /* * Install the signal handlers for suspend/resume. * * We add SIGUSR2 to the sa_mask field for the SIGUSR1 handler. That * avoids a race if one thread suspends the target while another resumes * that same target. (The SIGUSR2 signal cannot be delivered before the * target thread calls sigsuspend.) */ sigusr1.sa_flags = 0; sigusr1.sa_handler = suspend_signal_handler; sigemptyset (&sigusr1.sa_mask); sigaddset (&sigusr1.sa_mask, SIGUSR2); sigusr2.sa_flags = 0; sigusr2.sa_handler = resume_signal_handler; sigemptyset (&sigusr2.sa_mask); status = sigaction (SIGUSR1, &sigusr1, NULL); if (status == -1) errno_abort ("Installing suspend handler"); status = sigaction (SIGUSR2, &sigusr2, NULL); if (status == -1) errno_abort ("Installing resume handler"); return; }
int main (int argc, char *argv[]) { int status; char line[128]; int seconds; pid_t pid; char message[64]; while (1) { printf ("Alarm> "); if (fgets (line, sizeof (line), stdin) == NULL) exit (0); if (strlen (line) <= 1) continue; /* * Parse input line into seconds (%d) and a message * (%64[^\n]), consisting of up to 64 characters * separated from the seconds by whitespace. */ if (sscanf (line, "%d %64[^\n]", &seconds, message) < 2) { fprintf (stderr, "Bad command\n"); } else { pid = fork (); if (pid == (pid_t)-1) errno_abort ("Fork"); if (pid == (pid_t)0) { /* * If we're in the child, wait and then print a message */ sleep (seconds); printf ("(%d) %s\n", seconds, message); exit (0); } else { /* * In the parent, call waitpid() to collect any children that * have already terminated. */ do { pid = waitpid ((pid_t)-1, NULL, WNOHANG);//cast -1 as the type of pid_t if (pid == (pid_t)-1) errno_abort ("Wait for child"); } while (pid != (pid_t)0); } } } }
/* * Thread start routine that issues work queue requests. */ void *thread_routine (void *arg) { power_t *element; int count; unsigned int seed = (unsigned int)time (NULL); int status; /* * Loop, making requests. */ for (count = 0; count < ITERATIONS; count++) { element = (power_t*)malloc (sizeof (power_t)); if (element == NULL) errno_abort ("Allocate element"); element->value = rand_r (&seed) % 20; element->power = rand_r (&seed) % 7; DPRINTF (( "Request: %d^%d\n", element->value, element->power)); status = workq_add (&workq, (void*)element); if (status != 0) err_abort (status, "Add to work queue"); sleep (rand_r (&seed) % 5); } return NULL; }
int main (int argc, char *argv[]) { int status; char line[128]; alarm_t *alarm; pthread_t thread; while (1) { printf ("Alarm> "); if (fgets (line, sizeof (line), stdin) == NULL) exit (0); if (strlen (line) <= 1) continue; alarm = (alarm_t*)malloc (sizeof (alarm_t)); if (alarm == NULL) errno_abort ("Allocate alarm"); /* * Parse input line into seconds (%d) and a message * (%64[^\n]), consisting of up to 64 characters * separated from the seconds by whitespace. */ if (sscanf (line, "%d %64[^\n]", &alarm->seconds, alarm->message) < 2) { fprintf (stderr, "Bad command\n"); free (alarm); } else { status = pthread_create ( &thread, NULL, alarm_thread, alarm); if (status != 0) err_abort (status, "Create alarm thread"); } } }
/* * External interface to create a pipeline. All the * data is initialized and the threads created. They'll * wait for data. */ int pipe_create (pipe_t *pipe, int stages) { int pipe_index; stage_t **link = &pipe->head, *new_stage, *stage; int status; status = pthread_mutex_init (&pipe->mutex, NULL); if (status != 0) err_abort (status, "Init pipe mutex"); pipe->stages = stages; pipe->active = 0; for (pipe_index = 0; pipe_index <= stages; pipe_index++) { new_stage = (stage_t*)malloc (sizeof (stage_t)); if (new_stage == NULL) errno_abort ("Allocate stage"); status = pthread_mutex_init (&new_stage->mutex, NULL); if (status != 0) err_abort (status, "Init stage mutex"); status = pthread_cond_init (&new_stage->avail, NULL); if (status != 0) err_abort (status, "Init avail condition"); status = pthread_cond_init (&new_stage->ready, NULL); if (status != 0) err_abort (status, "Init ready condition"); new_stage->data_ready = 0; *link = new_stage; link = &new_stage->next; } *link = (stage_t*)NULL; /* Terminate list */ pipe->tail = new_stage; /* Record the tail */ /* * Create the threads for the pipe stages only after all * the data is initialized (including all links). Note * that the last stage doesn't get a thread, it's just * a receptacle for the final pipeline value. * * At this point, proper cleanup on an error would take up * more space than worthwhile in a "simple example", so * instead of cancelling and detaching all the threads * already created, plus the synchronization object and * memory cleanup done for earlier errors, it will simply * abort. */ for ( stage = pipe->head; stage->next != NULL; stage = stage->next) { status = pthread_create ( &stage->thread, NULL, pipe_stage, (void*)stage); if (status != 0) err_abort (status, "Create pipe stage"); } return 0; }
int main(int argc,char* argv[]) { int status; char line[128]; alarm_t* alarm; pthread_t thread; status=pthread_create(&thread,NULL,alarm_thread,NULL); if(status!=0) { err_abort(status,"Create alarm thread"); } while(1){ printf("Alarm> \n"); if(fgets(line,sizeof(line),stdin)==NULL) exit(0); if(strlen(line)<=1) continue; alarm=(alarm_t*)malloc(sizeof(alarm_t)); if(alarm==NULL) { errno_abort("Allocate alarm"); } if(sscanf(line,"%d %64[^\n]", &alarm->seconds,alarm->message)<2) { fprintf(stderr,"Bad command\n"); free(alarm); } else { status=pthread_mutex_lock(&alarm_mutex); if(status!=0) { err_abort(status,"Lock mutex"); } alarm->time=time(NULL)+alarm->seconds; alarm_insert(alarm); status=pthread_mutex_unlock(&alarm_mutex); if(status!=0) { err_abort(status,"Unlock mutex"); } } } return 0; }
/* * This routine writes a prompt to stdout (passed as the thread's * "arg"), and reads a response. All other I/O to stdin and stdout * is prevented by the file locks until both prompt and fgets are * complete. */ void *prompt_routine (void *arg) { char *prompt = (char*)arg; char *string; int len; string = (char*)malloc (128); if (string == NULL) errno_abort ("Alloc string"); flockfile (stdin); flockfile (stdout); printf (prompt); if (fgets (string, 128, stdin) == NULL) string[0] = '\0'; else { len = strlen (string); if (len > 0 && string[len-1] == '\n') string[len-1] = '\0'; } funlockfile (stdout); funlockfile (stdin); return (void*)string; }
/* * Thread start routine that uses pthread_once to dynamically * create a thread-specific data key. */ void *thread_routine (void *arg) { tsd_t *value; int status; status = pthread_once (&key_once, once_routine); if (status != 0) err_abort (status, "Once init"); value = (tsd_t*)malloc (sizeof (tsd_t)); if (value == NULL) errno_abort ("Allocate key value"); status = pthread_setspecific (tsd_key, value); if (status != 0) err_abort (status, "Set tsd"); printf ("%s set tsd value %p\n", arg, value); value->thread_id = pthread_self (); value->string = (char*)arg; value = (tsd_t*)pthread_getspecific (tsd_key); printf ("%s starting...\n", value->string); sleep (2); value = (tsd_t*)pthread_getspecific (tsd_key); printf ("%s done...\n", value->string); return NULL; }
/* * The thread start routine for crew threads. Waits until "go" * command, processes work items until requested to shut down. */ void *worker_routine (void *arg) { worker_p mine = (worker_t*)arg; crew_p crew = mine->crew; work_p work, new_work; struct stat filestat; struct dirent *entry; int status; /* * "struct dirent" is funny, because POSIX doesn't require * the definition to be more than a header for a variable * buffer. Thus, allocate a "big chunk" of memory, and use * it as a buffer. */ entry = (struct dirent*)malloc ( sizeof (struct dirent) + name_max); if (entry == NULL) errno_abort ("Allocating dirent"); status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock crew mutex"); /* * There won't be any work when the crew is created, so wait * until something's put on the queue. */ while (crew->work_count == 0) { status = pthread_cond_wait (&crew->go, &crew->mutex); if (status != 0) err_abort (status, "Wait for go"); } status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); DPRINTF (("Crew %d starting\n", mine->index)); /* * Now, as long as there's work, keep doing it. */ while (1) { /* * Wait while there is nothing to do, and * the hope of something coming along later. If * crew->first is NULL, there's no work. But if * crew->work_count goes to zero, we're done. */ status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock crew mutex"); DPRINTF (("Crew %d top: first is %#lx, count is %d\n", mine->index, crew->first, crew->work_count)); while (crew->first == NULL) { status = pthread_cond_wait (&crew->go, &crew->mutex); if (status != 0) err_abort (status, "Wait for work"); } DPRINTF (("Crew %d woke: %#lx, %d\n", mine->index, crew->first, crew->work_count)); /* * Remove and process a work item */ work = crew->first; crew->first = work->next; if (crew->first == NULL) crew->last = NULL; DPRINTF (("Crew %d took %#lx, leaves first %#lx, last %#lx\n", mine->index, work, crew->first, crew->last)); status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); /* * We have a work item. Process it, which may involve * queuing new work items. */ status = lstat (work->path, &filestat); if (S_ISLNK (filestat.st_mode)) printf ( "Thread %d: %s is a link, skipping.\n", mine->index, work->path); else if (S_ISDIR (filestat.st_mode)) { DIR *directory; struct dirent *result; /* * If the file is a directory, search it and place * all files onto the queue as new work items. */ directory = opendir (work->path); if (directory == NULL) { fprintf ( stderr, "Unable to open directory %s: %d (%s)\n", work->path, errno, strerror (errno)); continue; } while (1) { status = readdir_r (directory, entry, &result); if (status != 0) { fprintf ( stderr, "Unable to read directory %s: %d (%s)\n", work->path, status, strerror (status)); break; } if (result == NULL) break; /* End of directory */ /* * Ignore "." and ".." entries. */ if (strcmp (entry->d_name, ".") == 0) continue; if (strcmp (entry->d_name, "..") == 0) continue; new_work = (work_p)malloc (sizeof (work_t)); if (new_work == NULL) errno_abort ("Unable to allocate space"); new_work->path = (char*)malloc (path_max); if (new_work->path == NULL) errno_abort ("Unable to allocate path"); strcpy (new_work->path, work->path); strcat (new_work->path, "/"); strcat (new_work->path, entry->d_name); new_work->string = work->string; new_work->next = NULL; status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock mutex"); if (crew->first == NULL) { crew->first = new_work; crew->last = new_work; } else { crew->last->next = new_work; crew->last = new_work; } crew->work_count++; DPRINTF (( "Crew %d: add work %#lx, first %#lx, last %#lx, %d\n", mine->index, new_work, crew->first, crew->last, crew->work_count)); status = pthread_cond_signal (&crew->go); status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); } closedir (directory); } else if (S_ISREG (filestat.st_mode)) { FILE *search; char buffer[256], *bufptr, *search_ptr; /* * If this is a file, not a directory, then search * it for the string. */ search = fopen (work->path, "r"); if (search == NULL) fprintf ( stderr, "Unable to open %s: %d (%s)\n", work->path, errno, strerror (errno)); else { while (1) { bufptr = fgets ( buffer, sizeof (buffer), search); if (bufptr == NULL) { if (feof (search)) break; if (ferror (search)) { fprintf ( stderr, "Unable to read %s: %d (%s)\n", work->path, errno, strerror (errno)); break; } } search_ptr = strstr (buffer, work->string); if (search_ptr != NULL) { flockfile (stdout); printf ( "Thread %d found \"%s\" in %s\n", mine->index, work->string, work->path); #if 0 printf ("%s\n", buffer); #endif funlockfile (stdout); break; } } fclose (search); } } else fprintf ( stderr, "Thread %d: %s is type %o (%s))\n", mine->index, work->path, filestat.st_mode & S_IFMT, (S_ISFIFO (filestat.st_mode) ? "FIFO" : (S_ISCHR (filestat.st_mode) ? "CHR" : (S_ISBLK (filestat.st_mode) ? "BLK" : (S_ISSOCK (filestat.st_mode) ? "SOCK" : "unknown"))))); free (work->path); /* Free path buffer */ free (work); /* We're done with this */ /* * Decrement count of outstanding work items, and wake * waiters (trying to collect results or start a new * calculation) if the crew is now idle. * * It's important that the count be decremented AFTER * processing the current work item. That ensures the * count won't go to 0 until we're really done. */ status = pthread_mutex_lock (&crew->mutex); if (status != 0) err_abort (status, "Lock crew mutex"); crew->work_count--; DPRINTF (("Crew %d decremented work to %d\n", mine->index, crew->work_count)); if (crew->work_count <= 0) { DPRINTF (("Crew thread %d done\n", mine->index)); status = pthread_cond_broadcast (&crew->done); if (status != 0) err_abort (status, "Wake waiters"); status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); break; } status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock mutex"); } free (entry); return NULL; }
/* * Pass a file path to a work crew previously created * using crew_create */ int crew_start ( crew_p crew, char *filepath, char *search) { work_p request; int status; status = pthread_mutex_lock (&crew->mutex); if (status != 0) return status; /* * If the crew is busy, wait for them to finish. */ while (crew->work_count > 0) { status = pthread_cond_wait (&crew->done, &crew->mutex); if (status != 0) { pthread_mutex_unlock (&crew->mutex); return status; } } errno = 0; path_max = pathconf (filepath, _PC_PATH_MAX); if (path_max == -1) { if (errno == 0) path_max = 1024; /* "No limit" */ else errno_abort ("Unable to get PATH_MAX"); } errno = 0; name_max = pathconf (filepath, _PC_NAME_MAX); if (name_max == -1) { if (errno == 0) name_max = 256; /* "No limit" */ else errno_abort ("Unable to get NAME_MAX"); } DPRINTF (( "PATH_MAX for %s is %ld, NAME_MAX is %ld\n", filepath, path_max, name_max)); path_max++; /* Add null byte */ name_max++; /* Add null byte */ request = (work_p)malloc (sizeof (work_t)); if (request == NULL) errno_abort ("Unable to allocate request"); DPRINTF (("Requesting %s\n", filepath)); request->path = (char*)malloc (path_max); if (request->path == NULL) errno_abort ("Unable to allocate path"); strcpy (request->path, filepath); request->string = search; request->next = NULL; if (crew->first == NULL) { crew->first = request; crew->last = request; } else { crew->last->next = request; crew->last = request; } crew->work_count++; status = pthread_cond_signal (&crew->go); if (status != 0) { free (crew->first); crew->first = NULL; crew->work_count = 0; pthread_mutex_unlock (&crew->mutex); return status; } while (crew->work_count > 0) { status = pthread_cond_wait (&crew->done, &crew->mutex); if (status != 0) err_abort (status, "waiting for crew to finish"); } status = pthread_mutex_unlock (&crew->mutex); if (status != 0) err_abort (status, "Unlock crew mutex"); return 0; }
void *Consume (void *arg) /* Consumer thread function. */ { msg_block_t *pmb; statistics_t * ps; int my_number, tstatus; struct timespec timeout, delta; delta.tv_sec = 2; delta.tv_nsec = 0; /* Create thread-specific storage key */ tstatus = pthread_once (&once_control, once_init_function); if (tstatus != 0) err_abort (tstatus, "One time init failed"); pmb = (msg_block_t *)arg; /* Allocate storage for thread-specific statistics */ ps = calloc (sizeof(statistics_t), 1); if (ps == NULL) errno_abort ("Cannot allocate memory"); tstatus = pthread_setspecific (ts_key, ps); if (tstatus != 0) err_abort (tstatus, "Error setting ts storage"); ps->pmblock = pmb; /* Give this thread a unique number */ /* Note that the mutex is "overloaded" to protect data */ /* outside the message block structure */ tstatus = pthread_mutex_lock (&pmb->nGuard); if (tstatus != 0) err_abort (tstatus, "Lock error"); ps->th_number = thread_number++; tstatus = pthread_mutex_unlock (&pmb->nGuard); if (tstatus != 0) err_abort (tstatus, "Unlock error"); /* Consume the NEXT message when prompted by the user */ while (!pmb->fStop) { /* This is the only thread accessing stdin, stdout */ tstatus = pthread_mutex_lock (&pmb->nGuard); if (tstatus != 0) err_abort (tstatus, "Lock error"); /* Get the next message. Use a timed wait so as to be able */ /* to sample the stop flag peridically. */ do { pthread_get_expiration_np (&delta, &timeout); tstatus = pthread_cond_timedwait (&pmb->mReady, &pmb->nGuard, &timeout); if (tstatus != 0 && tstatus != ETIMEDOUT) err_abort (tstatus, "CV wait error"); } while (!pmb->f_ready && !pmb->fStop); if (!pmb->fStop) { /* printf ("Message received\n"); */ accumulate_statistics (); pmb->f_consumed = 1; pmb->f_ready = 0; } tstatus = pthread_cond_signal (&pmb->mconsumed); if (tstatus != 0) err_abort (tstatus, "Signal error"); tstatus = pthread_mutex_unlock (&pmb->nGuard); if (tstatus != 0) err_abort (tstatus, "Unlock error"); } /* Shutdown. Report the statistics */ tstatus = pthread_mutex_lock (&pmb->nGuard); if (tstatus != 0) err_abort (tstatus, "Lock error"); report_statistics (); tstatus = pthread_mutex_unlock (&pmb->nGuard); if (tstatus != 0) err_abort (tstatus, "Unlock error"); /* Terminate the consumer thread. The destructor will */ /* free the memory allocated for the statistics */ return NULL; }
void tty_server_request(int operation, int sync, const char *prompt, char *string) { int status; request_t *request; status = pthread_mutex_lock(&server.mutex); if (status != 0) { err_abort(status, "Lock server mutex"); } // check if server thread is already running if (!server.running) { pthread_t thread; pthread_attr_t detached_att; status = pthread_attr_init(&detached_att); if (status != 0) { err_abort(status, "Init detached attribute"); } // set detached attribute for server thread status = pthread_attr_setdetachstate(&detached_att, PTHREAD_CREATE_DETACHED); if (status != 0) { err_abort(status, "Set detach state"); } status = pthread_create(&thread, &detached_att, server_routine, NULL); if (status != 0) { err_abort(status, "Create server routine"); } server.running = 1; status = pthread_attr_destroy(&detached_att); if (status != 0) { fprintf(stderr, "Destroy detached attribute"); } } // add request request = malloc(sizeof(request_t)); if (request == NULL) { errno_abort("Allocate memory for request"); } request->next = NULL; request->operation = operation; request->synchronous = sync; // only init condition variable for sync request if (sync) { request->done_flag = 0; pthread_cond_init(&request->done, NULL); } if (prompt != NULL) { strncpy(request->prompt, prompt, PROMPT_MAX); } else { request->prompt[0] = '\0'; } if (operation == REQ_WRITE && string != NULL) { strncpy(request->text, string, TEXT_MAX); } else { request->text[0] = '\0'; } if (server.first == NULL) { server.first = server.last = request; } else { server.last->next = request; server.last = request; } pthread_cond_signal(&server.request); // destory condition variable for sync request if (sync) { while (!request->done_flag) { status = pthread_cond_wait(&request->done, &server.mutex); if (status != 0) { err_abort(status, "Wait on request done cond"); } } if (operation == REQ_READ) { if (strlen(request->text) > 0) { strncpy(string, request->text, TEXT_MAX); } } status = pthread_cond_destroy(&request->done); if (status != 0) { err_abort(status, "Destroy request done cond"); } free(request); } status = pthread_mutex_unlock(&server.mutex); if (status != 0) { err_abort(status, "Unlock server mutex"); } }
/* * Request an operation */ void tty_server_request ( int operation, int sync, const char *prompt, char *string) { request_t *request; int status; status = pthread_mutex_lock (&tty_server.mutex); if (status != 0) err_abort (status, "Lock server mutex"); if (!tty_server.running) { pthread_t thread; pthread_attr_t detached_attr; status = pthread_attr_init (&detached_attr); if (status != 0) err_abort (status, "Init attributes object"); status = pthread_attr_setdetachstate ( &detached_attr, PTHREAD_CREATE_DETACHED); if (status != 0) err_abort (status, "Set detach state"); tty_server.running = 1; status = pthread_create (&thread, &detached_attr, tty_server_routine, NULL); if (status != 0) err_abort (status, "Create server"); /* * Ignore an error in destroying the attributes object. * It's unlikely to fail, there's nothing useful we can * do about it, and it's not worth aborting the program * over it. */ pthread_attr_destroy (&detached_attr); } /* * Create and initialize a request structure. */ request = (request_t*)malloc (sizeof (request_t)); if (request == NULL) errno_abort ("Allocate request"); request->next = NULL; request->operation = operation; request->synchronous = sync; if (sync) { request->done_flag = 0; status = pthread_cond_init (&request->done, NULL); if (status != 0) err_abort (status, "Init request condition"); } if (prompt != NULL) strncpy (request->prompt, prompt, 32); else request->prompt[0] = '\0'; if (operation == REQ_WRITE && string != NULL) strncpy (request->text, string, 128); else request->text[0] = '\0'; /* * Add the request to the queue, maintaining the first and * last pointers. */ if (tty_server.first == NULL) { tty_server.first = request; tty_server.last = request; } else { (tty_server.last)->next = request; tty_server.last = request; } /* * Tell the server that a request is available. */ status = pthread_cond_signal (&tty_server.request); if (status != 0) err_abort (status, "Wake server"); /* * If the request was "synchronous", then wait for a reply. */ if (sync) { while (!request->done_flag) { status = pthread_cond_wait ( &request->done, &tty_server.mutex); if (status != 0) err_abort (status, "Wait for sync request"); } if (operation == REQ_READ) { if (strlen (request->text) > 0) strcpy (string, request->text); else string[0] = '\0'; } status = pthread_cond_destroy (&request->done); if (status != 0) err_abort (status, "Destroy request condition"); free (request); } status = pthread_mutex_unlock (&tty_server.mutex); if (status != 0) err_abort (status, "Unlock mutex"); }