Thread Programming Examples
Thread Programming Examples
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
Subsections
Using thr_create() and thr_join()
Arrays
Deadlock
Signal Handler
Interprocess Synchronization
The Producer / Consumer Problem
A Socket Server
Using Many Threads
Real-time Thread Example
POSIX Cancellation
Software Race Condition
Tgrep: Threadeds version of UNIX grep
Multithreaded Quicksort
Using
thr_create()
and
thr_join()
This example exercises the thr_create() and thr_join() calls. There is not a parent/child
relationship between threads as there is for processes. This can easily be seen in this
example, because threads are created and joined by many dierent threads in the
process. The example also shows how threads behave when created with dierent
attributes and options.
Threads can be created by any thread and joined by any other.
The main thread: In this example the main thread's sole purpose is to create new
threads. Threads A, B, and C are created by the main thread. Notice that thread B is
created suspended. After creating the new threads, the main thread exits. Also notice
that the main thread exited by calling thr_exit(). If the main thread had used the exit()
call, the whole process would have exited. The main thread's exit status and resources
are held until it is joined by thread C.
Thread A: The rst thing thread A does after it is created is to create thread D. Thread
A then simulates some processing and then exits, using thr_exit(). Notice that thread A
was created with the THR_DETACHED ag, so thread A's resources will be immediately
reclaimed upon its exit. There is no way for thread A's exit status to be collected by a
thr_join() call.
Thread B: Thread B was created in a suspended state, so it is not able to run until
thread D continues it by making the thr_continue() call. After thread B is continued, it
simulates some processing and then exits. Thread B's exit status and thread resources
are held until joined by thread E.
Thread C: The rst thing that thread C does is to create thread F. Thread C then joins
the main thread. This action will collect the main thread's exit status and allow the
main thread's resources to be reused by another thread. Thread C will block, waiting
for the main thread to exit, if the main thread has not yet called thr_exit(). After joining
the main thread, thread C will simulate some processing and then exit. Again, the exit
status and thread resources are held until joined by thread E.
1 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
multi_thr.c:
#define _REENTRANT
#include <stdio.h>
#include <thread.h>
/* Function prototypes for thread routines */
void *sub_a(void *);
void *sub_b(void *);
void *sub_c(void *);
void *sub_d(void *);
void *sub_e(void *);
void *sub_f(void *);
thread_t thr_a, thr_b, thr_c;
void main()
{
thread_t main_thr;
main_thr = thr_self();
printf("Main thread = %d\n", main_thr);
if (thr_create(NULL, 0, sub_b, NULL, THR_SUSPENDED|THR_NEW_LWP, &thr_b))
fprintf(stderr,"Can't create thr_b\n"), exit(1);
if (thr_create(NULL, 0, sub_a, (void *)thr_b, THR_NEW_LWP, &thr_a))
fprintf(stderr,"Can't create thr_a\n"), exit(1);
if (thr_create(NULL, 0, sub_c, (void *)main_thr, THR_NEW_LWP, &thr_c))
fprintf(stderr,"Can't create thr_c\n"), exit(1);
printf("Main Created threads A:%d B:%d C:%d\n", thr_a, thr_b, thr_c);
printf("Main Thread exiting...\n");
thr_exit((void *)main_thr);
}
void *sub_a(void *arg)
{
thread_t thr_b = (thread_t) arg;
thread_t thr_d;
int i;
printf("A: In thread A...\n");
if (thr_create(NULL, 0, sub_d, (void *)thr_b, THR_NEW_LWP, &thr_d))
2 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
3 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
thr_exit((void *)55);
}
Arrays
This example uses a data structure that contains multiple arrays of data. Multiple
threads will concurrently vie for access to the arrays. To control this access, a mutex
variable is used within the data structure to lock the entire array and serialize the
access to the data.
The main thread rst initializes the data structure and the mutex variable. It then sets
a level of concurrency and creates the worker threads. The main thread then blocks by
joining all the threads. When all the threads have exited, the main thread prints the
results.
The worker threads modify the shared data structure from within a loop. Each time the
threads need to modify the shared data, they lock the mutex variable associated with
the shared data. After modifying the data, the threads unlock the mutex, allowing
another thread access to the data.
This example may look quite simple, but it shows how important it is to control access
to a simple, shared data structure. The results can be quite dierent if the mutex
variable is not used.
The source to
array.c:
#define _REENTRANT
#include <stdio.h>
#include <thread.h>
4 of 42
11/10/2016 07:49 PM
/* sample array
struct {
mutex_t
int
float
} Data;
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
data structure */
data_lock[5];
int_val[5];
float_val[5];
/* thread function */
void *Add_to_Value();
main()
{
int i;
/* initialize the mutexes and data */
for (i=0; i<5; i++) {
mutex_init(&Data.data_lock[i], USYNC_THREAD, 0);
Data.int_val[i] = 0;
Data.float_val[i] = 0;
}
/* set concurrency and create the threads */
thr_setconcurrency(4);
for (i=0; i<5; i++)
thr_create(NULL, 0, Add_to_Value, (void *)(2*i), 0, NULL);
/* wait till all threads have finished */
for (i=0; i<5; i++)
thr_join(0,0,0);
/* print the results */
printf("Final Values.....\n");
for (i=0; i<5; i++) {
printf("integer value[%d] =\t%d\n", i, Data.int_val[i]);
printf("float value[%d] =\t%.0f\n\n", i, Data.float_val[i]);
}
return(0);
}
/* Threaded routine */
void *Add_to_Value(void *arg)
{
int inval = (int) arg;
int i;
for (i=0;i<10000;i++){
mutex_lock(&Data.data_lock[i%5]);
Data.int_val[i%5] += inval;
Data.float_val[i%5] += (float) 1.5 * inval;
mutex_unlock(&Data.data_lock[i%5]);
}
return((void *)0);
}
Deadlock
This example demonstrates how a deadlock can occur in multithreaded programs that
use synchronization variables. In this example a thread is created that continually adds
a value to a global variable. The thread uses a mutex lock to protect the global data.
The main thread creates the counter thread and then loops, waiting for user input.
When the user presses the Return key, the main thread suspends the counter thread
and then prints the value of the global variable. The main thread prints the value of the
global variable under the protection of a mutex lock.
The problem arises in this example when the main thread suspends the counter thread
while the counter thread is holding the mutex lock. After the main thread suspends the
counter thread, it tries to lock the mutex variable. Since the mutex variable is already
held by the counter thread, which is suspended, the main thread deadlocks.
5 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
This example may run ne for a while, as long as the counter thread just happens to be
suspended when it is not holding the mutex lock. The example demonstrates how
tricky some programming issues can be when you deal with threads.
The source to
susp_lock.c
#define _REENTRANT
#include <stdio.h>
#include <thread.h>
/* Prototype for thread subroutine */
void *counter(void *);
int count;
mutex_t count_lock;
main()
{
char str[80];
thread_t ctid;
/* create the thread counter subroutine */
thr_create(NULL, 0, counter, 0, THR_NEW_LWP|THR_DETACHED, &ctid);
while(1) {
gets(str);
thr_suspend(ctid);
mutex_lock(&count_lock);
printf("\n\nCOUNT = %d\n\n", count);
mutex_unlock(&count_lock);
thr_continue(ctid);
}
return(0);
}
void *counter(void *arg)
{
int i;
while (1) {
printf("."); fflush(stdout);
mutex_lock(&count_lock);
count++;
for (i=0;i<50000;i++);
mutex_unlock(&count_lock);
for (i=0;i<50000;i++);
}
return((void *)0);
}
Signal Handler
This example shows how easy it is to handle signals in multithreaded programs. In
most programs, a dierent signal handler would be needed to service each type of
signal that you wanted to catch. Writing each of the signal handlers can be time
consuming and can be a real pain to debug.
This example shows how you can implement a signal handler thread that will service
all asynchronous signals that are sent to your process. This is an easy way to deal with
signals, because only one thread is needed to handle all the signals. It also makes it
easy when you create new threads within the process, because you need not worry
about signals in any of the threads.
First, in the main thread, mask out all signals and then create a signal handling
thread. Since threads inherit the signal mask from their creator, any new threads
created after the new signal mask will also mask all signals. This idea is key, because
the only thread that will receive signals is the one thread that does not block all the
6 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
signals.
The signal handler thread waits for all incoming signals with the sigwait() call. This
call unmasks the signals given to it and then blocks until a signal arrives. When a
signal arrives, sigwait() masks the signals again and then returns with the signal ID of
the incoming signal.
You can extend this example for use in your application code to handle all your signals.
Notice also that this signal concept could be added in your existing nonthreaded code
as a simpler way to deal with signals.
The source to
thr_sig.c
#define _REENTRANT
#include <stdio.h>
#include <thread.h>
#include <signal.h>
#include <sys/types.h>
void *signal_hand(void *);
main()
{
sigset_t set;
/* block all signals in main thread. Any other threads that are
created after this will also block all signals */
sigfillset(&set);
thr_sigsetmask(SIG_SETMASK, &set, NULL);
/* create a signal handler thread. This thread will catch all
signals and decide what to do with them. This will only
catch nondirected signals. (I.e., if a thread causes a SIGFPE
then that thread will get that signal. */
thr_create(NULL, 0, signal_hand, 0, THR_NEW_LWP|THR_DAEMON|THR_DETACHED, NULL);
while (1) {
/*
Do your normal processing here....
*/
} /* end of while */
return(0);
}
void *signal_hand(void *arg)
{
sigset_t set;
int sig;
sigfillset(&set); /* catch all signals */
while (1) {
/* wait for a signal to arrive */
switch (sig=sigwait(&set)) {
/* here you would add whatever signal you needed to catch */
case SIGINT : {
printf("Interrupted with signal %d, exiting...\n", sig);
exit(0);
}
default : printf("GOT A SIGNAL = %d\n", sig);
} /* end of switch */
} /* end of while */
return((void *)0);
} /* end of signal_hand */
sig_kill.c:
/*
* Multithreaded Demo Source
*
* Copyright (C) 1995 by Sun Microsystems, Inc.
7 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
94043
thread_t
one_tid, two_tid, main_thread;
void
*first_thread();
void
*second_thread();
ExitHandler(int);
static
int
int
mutex_t
first_mutex, second_mutex;
first_active = 1 ;
second_active = 1;
main()
{
int i;
struct sigaction act;
act.sa_handler = ExitHandler;
(void) sigemptyset(&act.sa_mask);
(void) sigaction(SIGTERM, &act, NULL);
mutex_init(&first_mutex, 0 , 0);
mutex_init(&second_mutex, 0 , 0);
main_thread = thr_self();
thr_create(NULL,0,first_thread,0,THR_NEW_LWP,&one_tid);
thr_create(NULL,0,second_thread,0,THR_NEW_LWP,&two_tid);
for (i = 0; i < 10; i++){
fprintf(stderr, "main loop: %d\n", i);
if (i == 5) {
thr_kill(one_tid, SIGTERM);
}
sleep(3);
}
thr_kill(two_tid, SIGTERM);
sleep(5);
fprintf(stderr, "main exit\n");
8 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
}
static void *first_thread()
{
int i = 0;
fprintf(stderr, "first_thread id: %d\n", thr_self());
while (first_active){
fprintf(stderr, "first_thread: %d\n", i++);
sleep(2);
}
fprintf(stderr, "first_thread exit\n");
}
static void *second_thread()
{
int i = 0;
fprintf(stderr, "second_thread id: %d\n", thr_self());
while (second_active){
fprintf(stderr, "second_thread: %d\n", i++);
sleep(3);
}
fprintf(stderr, "second_thread exit\n");
}
void ExitHandler(int sig)
{
thread_t id;
id = thr_self();
fprintf(stderr, "ExitHandler thread id: %d\n", id);
thr_exit(0);
}
Interprocess Synchronization
This example uses some of the synchronization variables available in the threads
library to synchronize access to a resource shared between two processes. The
synchronization variables used in the threads library are an advantage over standard
IPC synchronization mechanisms because of their speed. The synchronization variables
in the threads libraries have been tuned to be very lightweight and very fast. This
speed can be an advantage when your application is spending time synchronizing
between processes.
This example shows how semaphores from the threads library can be used between
processes. Note that this program does not use threads; it is just using the lightweight
semaphores available from the threads library.
When using synchronization variables between processes, it is important to make sure
that only one process initializes the variable. If both processes try to initialize the
synchronization variable, then one of the processes will overwrite the state of the
variable set by the other process.
The source to
#include
#include
#include
#include
#include
#include
ipc.c
<stdio.h>
<fcntl.h>
<sys/mman.h>
<synch.h>
<sys/types.h>
<unistd.h>
9 of 42
i, j, fd;
*buf;
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
10 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
The example copies a le by reading data into a shared buer (producer) and then
writing data out to the new le (consumer). The Buf data structure is used to hold both
the buered data and the condition variables that control the ow of the data.
The main thread opens both les, initializes the Buf data structure, creates the
consumer thread, and then assumes the role of the producer. The producer reads data
from the input le, then places the data into an open buer position. If no buer
positions are available, then the producer waits via the cond_wait() call. After the
producer has read all the data from the input le, it closes the le and waits for (joins)
the consumer thread.
The consumer thread reads from a shared buer and then writes the data to the
output le. If no buers positions are available, then the consumer waits for the
producer to ll a buer position. After the consumer has read all the data, it closes the
output le and exits.
If the input le and the output le were residing on dierent physical disks, then this
example could execute the reads and writes in parallel. This parallelism would
signicantly increase the throughput of the example through the use of threads.
The source to
prod_cons.c:
#define _REEENTRANT
#include <stdio.h>
#include <thread.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/uio.h>
#define BUFSIZE 512
#define BUFCNT 4
/* this is the data structure that is used between the producer
and consumer threads */
struct {
char buffer[BUFCNT][BUFSIZE];
int byteinbuf[BUFCNT];
mutex_t buflock;
mutex_t donelock;
cond_t adddata;
cond_t remdata;
int nextadd, nextrem, occ, done;
} Buf;
/* function prototype */
void *consumer(void *);
main(int argc, char **argv)
{
int ifd, ofd;
thread_t cons_thr;
/* check the command line arguments */
if (argc != 3)
printf("Usage: %s <infile> <outfile>\n", argv[0]), exit(0);
/* open the input file for the producer to use */
if ((ifd = open(argv[1], O_RDONLY)) == -1)
{
fprintf(stderr, "Can't open file %s\n", argv[1]);
exit(1);
}
/* open the output file for the consumer to use */
if ((ofd = open(argv[2], O_WRONLY|O_CREAT, 0666)) == -1)
{
fprintf(stderr, "Can't open file %s\n", argv[2]);
exit(1);
}
/* zero the counters */
Buf.nextadd = Buf.nextrem = Buf.occ = Buf.done = 0;
/* set the thread concurrency to 2 so the producer and consumer can
11 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
run concurrently */
thr_setconcurrency(2);
/* create the consumer thread */
thr_create(NULL, 0, consumer, (void *)ofd, NULL, &cons_thr);
/* the producer ! */
while (1) {
/* lock the mutex */
mutex_lock(&Buf.buflock);
/* check to see if any buffers are empty */
/* If not then wait for that condition to become true */
while (Buf.occ == BUFCNT)
cond_wait(&Buf.remdata, &Buf.buflock);
/* read from the file and put data into a buffer */
Buf.byteinbuf[Buf.nextadd] = read(ifd,Buf.buffer[Buf.nextadd],BUFSIZE);
/* check to see if done reading */
if (Buf.byteinbuf[Buf.nextadd] == 0) {
/* lock the done lock */
mutex_lock(&Buf.donelock);
/* set the done flag and release the mutex lock */
Buf.done = 1;
mutex_unlock(&Buf.donelock);
/* signal the consumer to start consuming */
cond_signal(&Buf.adddata);
/* release the buffer mutex */
mutex_unlock(&Buf.buflock);
/* leave the while looop */
break;
}
/* set the next buffer to fill */
Buf.nextadd = ++Buf.nextadd % BUFCNT;
/* increment the number of buffers that are filled */
Buf.occ++;
/* signal the consumer to start consuming */
cond_signal(&Buf.adddata);
/* release the mutex */
mutex_unlock(&Buf.buflock);
}
close(ifd);
/* wait for the consumer to finish */
thr_join(cons_thr, 0, NULL);
/* exit the program */
return(0);
}
12 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
A Socket Server
The socket server example uses threads to implement a "standard" socket port server.
The example shows how easy it is to use thr_create() calls in the place of fork() calls in
existing programs.
A standard socket server should listen on a socket port and, when a message arrives,
fork a process to service the request. Since a fork() system call would be used in a
nonthreaded program, any communication between the parent and child would have to
be done through some sort of interprocess communication.
We can replace the fork() call with a thr_create() call. Doing so oers a few advantages:
thr_create() can create a thread much faster then a fork() could create a new process,
and any communication between the server and the new thread can be done with
common variables. This technique makes the implementation of the socket server
much easier to understand and should also make it respond much faster to incoming
requests.
The server program rst sets up all the needed socket information. This is the basic
setup for most socket servers. The server then enters an endless loop, waiting to
service a socket port. When a message is sent to the socket port, the server wakes up
and creates a new thread to handle the request. Notice that the server creates the new
thread as a detached thread and also passes the socket descriptor as an argument to
the new thread.
The newly created thread can then read or write, in any fashion it wants, to the socket
descriptor that was passed to it. At this point the server could be creating a new
thread or waiting for the next message to arrive. The key is that the server thread does
not care what happens to the new thread after it creates it.
In our example, the created thread reads from the socket descriptor and then
increments a global variable. This global variable keeps track of the number of
requests that were made to the server. Notice that a mutex lock is used to protect
access to the shared global variable. The lock is needed because many threads might
try to increment the same variable at the same time. The mutex lock provides serial
access to the shared variable. See how easy it is to share information among the new
threads! If each of the threads were a process, then a signicant eort would have to
be made to share this information among the processes.
The client piece of the example sends a given number of messages to the server. This
client code could also be run from dierent machines by multiple users, thus
increasing the need for concurrency in the server process.
The source code to
13 of 42
soc_server.c:
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
#define _REENTRANT
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/uio.h>
#include <unistd.h>
#include <thread.h>
/* the TCP port that is used for this example */
#define TCP_PORT
6500
/* function prototypes and global variables */
void *do_chld(void *);
mutex_t lock;
int
service_count;
main()
{
int
sockfd, newsockfd, clilen;
struct sockaddr_in cli_addr, serv_addr;
thread_t chld_thr;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
fprintf(stderr,"server: can't open stream socket\n"), exit(0);
memset((char *) &serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(TCP_PORT);
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) <
0)
fprintf(stderr,"server: can't bind local address\n"), exit(0);
/* set the level of thread concurrency we desire */
thr_setconcurrency(5);
listen(sockfd, 5);
for(;;){
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr,
&clilen);
if(newsockfd < 0)
fprintf(stderr,"server: accept error\n"), exit(0);
/* create a new thread to process the incomming request */
thr_create(NULL, 0, do_chld, (void *) newsockfd, THR_DETACHED,
&chld_thr);
/* the server is now free to accept another socket request */
}
return(0);
}
/*
This is the routine that is executed from a new thread
*/
void *do_chld(void *arg)
{
int
mysocfd = (int) arg;
char
data[100];
int
i;
printf("Child thread [%d]: Socket number = %d\n", thr_self(), mysocfd);
/* read from the given socket */
read(mysocfd, data, 40);
printf("Child thread [%d]: My data = %s\n", thr_self(), data);
/* simulate some processing */
for (i=0;i<1000000*thr_self();i++);
printf("Child [%d]: Done Processing...\n", thr_self());
/* use a mutex to update the global service counter */
mutex_lock(&lock);
14 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
service_count++;
mutex_unlock(&lock);
printf("Child thread [%d]: The total sockets served = %d\n", thr_self(), service_count);
/* close the socket and exit this thread */
close(mysocfd);
thr_exit((void *)0);
}
many_thr.c:
#define _REENTRANT
#include <stdio.h>
#include <stdlib.h>
#include <thread.h>
/* function prototypes and global varaibles */
void *thr_sub(void *);
mutex_t lock;
main(int argc, char **argv)
{
int i, thr_count = 100;
char buf;
/* check to see if user passed an argument
-- if so, set the number of threads to the value
passed to the program */
if (argc == 2) thr_count = atoi(argv[1]);
printf("Creating %d threads...\n", thr_count);
15 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
16 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
The example creates a new thread from the main thread. This new thread is then
promoted to the real-time class by looking up the real-time class ID and then setting a
real-time priority for the thread. After the thread is running in realtime, it simulates
some processing. Since a thread in the real-time class can have an innite time
quantum, the process is allowed to stay on a CPU as long as it likes. The time quantum
is the amount of time a thread is allowed to stay running on a CPU. For the
timesharing class, the time quantum (time-slice) is 1/100th of a second by default.
In this example, we set the time quantum for the real-time thread to innity. That is, it
can stay running as long as it likes; it will not be preempted or scheduled o the CPU.
If you run this example on a UP machine, it will have the eect of stopping your system
for a few seconds while the thread simulates its processing. The system does not
actually stop, it is just working in the real-time thread. When the real-time thread
nishes its processing, it exits and the system returns to normal.
Using real-time threads can be quite useful when you need an extremely high priority
and response time but can also cause big problems if it not used properly. Also note
that this example must be run as root or have root execute permissions.
The source to
rt_thr.c:
#define _REENTRANT
#include <stdio.h>
#include <thread.h>
#include <string.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
/* thread prototype */
void *rt_thread(void *);
main()
{
/* create the thread that will run in realtime */
thr_create(NULL, 0, rt_thread, 0, THR_DETACHED, 0);
/* loop here forever, this thread is the TS scheduling class */
while (1) {
printf("MAIN: In time share class... running\n");
sleep(1);
}
return(0);
}
/*
This is the routine that is called by the created thread
*/
void *rt_thread(void *arg)
{
pcinfo_t pcinfo;
pcparms_t pcparms;
int i;
/* let the main thread run for a bit */
sleep(4);
/* get the class ID for the real-time class */
strcpy(pcinfo.pc_clname, "RT");
if (priocntl(0, 0, PC_GETCID, (caddr_t)&pcinfo) == -1)
fprintf(stderr, "getting RT class id\n"), exit(1);
/* set up the real-time parameters */
pcparms.pc_cid = pcinfo.pc_cid;
((rtparms_t *)pcparms.pc_clparms)->rt_pri = 10;
((rtparms_t *)pcparms.pc_clparms)->rt_tqnsecs = 0;
/* set an infinite time quantum */
((rtparms_t *)pcparms.pc_clparms)->rt_tqsecs = RT_TQINF;
/* move this thread to the real-time scheduling class */
if (priocntl(P_LWPID, P_MYID, PC_SETPARMS, (caddr_t)&pcparms) == -1)
fprintf(stderr, "Setting RT mode\n"), exit(1);
/* simulate some processing */
17 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
for (i=0;i<100000000;i++);
printf("RT_THREAD: NOW EXITING...\n");
thr_exit((void *)0);
}
POSIX Cancellation
This example uses the POSIX thread cancellation capability to kill a thread that is no
longer needed. Random termination of a thread can cause problems in threaded
applications, because a thread may be holding a critical lock when it is terminated.
Since the lock was help before the thread was terminated, another thread may
deadlock, waiting for that same lock. The thread cancellation capability enables you to
control when a thread can be terminated. The example also demonstrates the
capabilities of the POSIX thread library in implementing a program that performs a
multithreaded search.
This example simulates a multithreaded search for a given number by taking random
guesses at a target number. The intent here is to simulate the same type of search that
a database might execute. For example, a database might create threads to start
searching for a data item; after some amount of time, one or more threads might
return with the target data item.
If a thread guesses the number correctly, there is no need for the other threads to
continue their search. This is where thread cancellation can help. The thread that nds
the number rst should cancel the other threads that are still searching for the item
and then return the results of the search.
The threads involved in the search can call a cleanup function that can clean up the
threads resources before it exits. In this case, the cleanup function prints the progress
of the thread when it was cancelled.
The source to
#define
#include
#include
#include
#include
#include
posix_cancel.c:
_REENTRANT
<stdio.h>
<unistd.h>
<stdlib.h>
<sys/types.h>
<pthread.h>
18 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
19 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
break;
}
/* every 100 tries check to see if the thread has been cancelled
if the thread has not been cancelled then yield the thread's
LWP to another thread that may be able to run */
if (i%100 == 0) {
pthread_testcancel();
sched_yield();
}
}
/* The only way we can get here is when the thread breaks out
of the while loop. In this case the thread that makes it here
has found the number we are looking for and does not need to run
the thread cleanup function. This is why the pthread_cleanup_pop
function is called with a 0 argument; this will pop the cleanup
function off the stack without executing it */
pthread_cleanup_pop(0);
return((void *)0);
}
Tgrep:
20 of 42
sw_race.c
grep
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
is a multi-threaded version of grep. Tgrep supports all but the -w (word search)
options of the normal grep command, and a few options that are only avaliable to Tgrep.
The real change from grep, is that Tgrep will recurse down through sub-directories and
search all les for the target string. Tgrep searches les like the following command:
Tgrep
Tgrep
directory)
Tgrep
would be
gets the results almost four times faster. The numbers above where gathered on a
SS20 running 5.5 (build 18) with 4 50MHz CPUs.
Tgrep
You can also lter the les that you want Tgrep to search like you can with nd. The
next two commands do the same thing, just Tgrep gets it done faster.
find . -name "*.c" -exec grep thr_create /dev/null {} \;
and
{\tt Tgrep} -p '.*\.c$' thr_create
The -p option will allow Tgrep to search only les that match the "regular expression"
le pattern string. This option does NOT use shell expression, so to stop Tgrep from
seeing a le named foobar.cyou must add the "$" meta character to the pattern and
escape the real "." character.
Some of the other Tgrep only options are -r, -C, -P, -e, -B, -S and -Z. The -r option stops
Tgrep from searching any sub-directories, in other words, search only the local
directory, but -l was taken. The -C option will search for and print "continued" lines like
you nd in Makele. Note the dierences in the results of grep and Tgrep run in the
current directory.
The Tgrep output prints the continued lines that ended with the "character. In the case
of grep I would not have seen the three values assigned to SUBDIRS, but Tgrep shows
them to me (Common, Solaris, Posix).
The -P option I use when I am sending the output of a long search to a le and want to
see the "progress" of the search. The -P option will print a "." (dot) on stderr for every
le (or groups of les depending on the value of the -P argument) Tgrep searches.
The -e option will change the way Tgrep uses the target string. Tgrep uses two dierent
patter matching systems. The rst (with out the -e option) is a literal string match call
Boyer-Moore. If the -e option is used, then a MT-Safe PD version of regular expression
is used to search for the target string as a regexp with meta characters in it. The
regular expression method is slower, but Tgrep needed the functionality. The -Z option
will print help on the meta characters Tgrep uses.
The -B option tells Tgrep to use the value of the environment variable called TGLIMIT to
limit the number of threads it will use during a search. This option has no aect if
TGLIMIT is not set. Tgrep can "eat" a system alive, so the -B option was a way to run
Tgrep on a system with out having other users scream at you.
21 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
The last new option is -S. If you want to see how things went while Tgrep was searching,
you can use this option to print statistic about the number of les, lines, bytes,
matches, threads created, etc.
Here is an example of the -S options output. (again run in the current directory)
% {\tt Tgrep} -S zimzap
----------------- {\tt Tgrep} Stats. -------------------Number of directories searched:
7
Number of files searched:
37
Number of lines searched:
9504
Number of matching lines to target:
0
Number of cascade threads created:
7
Number of search threads created:
20
Number of search threads from pool:
17
Search thread pool hit rate:
45.95%
Search pool overall size:
20
Search pool size limit:
58
Number of search threads destroyed:
0
Max # of threads running concurrenly:
20
Total run time, in seconds.
1
Work stopped due to no FD's: (058)
0 Times, 0.00%
Work stopped due to no work on Q:
19 Times, 43.18%
Work stopped due to TGLIMITS: (Unlimited) 0 Times, 0.00%
---------------------------------------------------%
For more information on the usage and options, see the man page
The
Tgrep.c
Tgrep
22 of 42
#define
#define
#define
#define
PATH_MAX
HOLD_FDS
UNLIMITED
MAXREGEXP
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
FB_BLOCK
FC_COUNT
FH_HOLDNAME
FI_IGNCASE
FL_NAMEONLY
FN_NUMBER
FS_NOERROR
FV_REVERSE
FW_WORD
FR_RECUR
FU_UNSORT
0x00001
0x00002
0x00004
0x00008
0x00010
0x00020
0x00040
0x00080
0x00100
0x00200
0x00400
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
FX_STDIN
TG_BATCH
TG_FILEPAT
FE_REGEXP
FS_STATS
FC_LINE
TG_PROGRESS
0x00800
0x01000
0x02000
0x04000
0x08000
0x10000
0x20000
#define FILET
#define DIRT
#define ALPHASIZ
1
2
128
/*
* New data types
*/
typedef struct work_st {
char
*path;
int
tp;
struct work_st
*next;
} work_t;
typedef struct out_st {
char
*line;
int
line_count;
long
byte_count;
struct out_st
*next;
} out_t;
typedef struct bm_pattern {
/* Boyer short
p_m;
/*
short
p_r[ALPHASIZ]; /*
short
*p_R;
/*
char
*p_pat;
/*
} BM_PATTERN;
Moore pattern
length of pattern string
"r" vector
"R" vector
pattern string
*/
*/
*/
*/
*/
/*
* Prototypes
*/
/* bmpmatch.c */
extern BM_PATTERN *bm_makepat(char *);
extern char *bm_pmatch(BM_PATTERN *, register char *);
extern void bm_freepat(BM_PATTERN *);
/* pmatch.c */
extern char *pmatch(register PATTERN *, register char *, int *);
extern PATTERN *makepat(char *string, char *);
extern void freepat(register PATTERN *);
extern void printpat(PATTERN *);
#include "proto.h"
mutex_t global_count_lk;
int
global_count = 0;
NOTE(MUTEX_PROTECTS_DATA(global_count_lk, global_count))
NOTE(DATA_READABLE_WITHOUT_LOCK(global_count)) /* see prnt_stats() */
work_t *work_q = NULL;
cond_t work_q_cv;
mutex_t work_q_lk;
int
all_done = 0;
int
work_cnt = 0;
int
current_open_files = 0;
int
tglimit = UNLIMITED;
/* if -B limit the number of threads */
NOTE(MUTEX_PROTECTS_DATA(work_q_lk, work_q all_done work_cnt \
current_open_files tglimit))
work_t *search_q = NULL;
mutex_t search_q_lk;
23 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
cond_t search_q_cv;
int
search_pool_cnt = 0;
/* the count in the pool now */
int
search_thr_limit = 0;
/* the max in the pool */
NOTE(MUTEX_PROTECTS_DATA(search_q_lk, search_q search_pool_cnt))
NOTE(DATA_READABLE_WITHOUT_LOCK(search_pool_cnt)) /* see prnt_stats() */
NOTE(READ_ONLY_DATA(search_thr_limit))
work_t *cascade_q = NULL;
mutex_t cascade_q_lk;
cond_t cascade_q_cv;
int
cascade_pool_cnt = 0;
int
cascade_thr_limit = 0;
NOTE(MUTEX_PROTECTS_DATA(cascade_q_lk, cascade_q cascade_pool_cnt))
NOTE(DATA_READABLE_WITHOUT_LOCK(cascade_pool_cnt)) /* see prnt_stats() */
NOTE(READ_ONLY_DATA(cascade_thr_limit))
int
running = 0;
mutex_t running_lk;
NOTE(MUTEX_PROTECTS_DATA(running_lk, running))
sigset_t set, oldset;
NOTE(READ_ONLY_DATA(set oldset))
mutex_t stat_lk;
time_t st_start = 0;
int
st_dir_search = 0;
int
st_file_search = 0;
int
st_line_search = 0;
int
st_cascade = 0;
int
st_cascade_pool = 0;
int
st_cascade_destroy = 0;
int
st_search = 0;
int
st_pool = 0;
int
st_maxrun = 0;
int
st_worknull = 0;
int
st_workfds = 0;
int
st_worklimit = 0;
int
st_destroy = 0;
NOTE(MUTEX_PROTECTS_DATA(stat_lk, st_start st_dir_search st_file_search \
st_line_search st_cascade st_cascade_pool \
st_cascade_destroy st_search st_pool st_maxrun \
st_worknull st_workfds st_worklimit st_destroy))
int
progress_offset = 1;
NOTE(READ_ONLY_DATA(progress_offset))
mutex_t output_print_lk;
/* output_print_lk used to print multi-line output only */
int
progress = 0;
NOTE(MUTEX_PROTECTS_DATA(output_print_lk, progress))
unsigned int
flags = 0;
int
regexp_cnt = 0;
char
*string[MAXREGEXP];
int
debug = 0;
int
use_pmatch = 0;
char
file_pat[255]; /* file patten match */
PATTERN *pm_file_pat; /* compiled file target string (pmatch()) */
NOTE(READ_ONLY_DATA(flags regexp_cnt string debug use_pmatch \
file_pat pm_file_pat))
/*
* Locking ording.
*/
NOTE(LOCK_ORDER(output_print_lk stat_lk))
/*
* Main: This is where the fun starts
*/
int
main(int argc, char **argv)
{
int
c,out_thr_flags;
long
max_open_files = 0l, ncpus = 0l;
extern int optind;
extern char *optarg;
NOTE(READ_ONLY_DATA(optind optarg))
int
prio = 0;
struct stat sbuf;
thread_t
tid,dtid;
void
*status;
24 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
*e = NULL, *d = NULL; /* for debug flags */
debug_file = 0;
err = 0, i = 0, pm_file_len = 0;
*work;
restart_cnt = 10;
flags = FR_RECUR;
/* the default */
#endif
25 of 42
d = optarg;
fprintf(stderr,"tgrep: Debug on at level(s) ");
while (*d) {
for (i=0; i<9; i++)
if (debug_set[i].level == *d) {
debug_levels |= debug_set[i].flag;
fprintf(stderr,"%c ",debug_set[i].level);
break;
}
d++;
}
fprintf(stderr,"\n");
break;
case 'f':
debug_file = atoi(optarg);
break;
/* DEBUG */
case 'B':
flags |= TG_BATCH;
if ((e = getenv("TGLIMIT"))) {
tglimit = atoi(e);
}
else {
if (!(flags & FS_NOERROR)) /* order dependent! */
fprintf(stderr,"env TGLIMIT not set, overriding -B\n");
flags &= ~TG_BATCH;
}
break;
case 'p':
flags |= TG_FILEPAT;
strcpy(file_pat,optarg);
pm_file_pat = makepat(file_pat,NULL);
break;
case 'P':
flags |= TG_PROGRESS;
progress_offset = atoi(optarg);
break;
case 'S': flags |= FS_STATS;
break;
case 'b': flags |= FB_BLOCK;
break;
case 'c': flags |= FC_COUNT;
break;
case 'h': flags |= FH_HOLDNAME; break;
case 'i': flags |= FI_IGNCASE; break;
case 'l': flags |= FL_NAMEONLY; break;
case 'n': flags |= FN_NUMBER;
break;
case 's': flags |= FS_NOERROR; break;
case 'v': flags |= FV_REVERSE; break;
case 'w': flags |= FW_WORD;
break;
case 'r': flags &= ~FR_RECUR;
break;
case 'C': flags |= FC_LINE;
break;
case 'e':
if (regexp_cnt == MAXREGEXP) {
fprintf(stderr,"Max number of regexp's (%d) exceeded!\n",
MAXREGEXP);
exit(1);
}
flags |= FE_REGEXP;
if ((string[regexp_cnt] =(char *)malloc(strlen(optarg)+1))==NULL){
fprintf(stderr,"tgrep: No space for search string(s)\n");
exit(1);
}
memset(string[regexp_cnt],0,strlen(optarg)+1);
strcpy(string[regexp_cnt],optarg);
regexp_cnt++;
break;
case 'z':
case 'Z': regexp_usage();
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
break;
case 'H':
case '?':
default : usage();
}
}
if (!(flags & FE_REGEXP)) {
if (argc - optind < 1) {
fprintf(stderr,"tgrep: Must supply a search string(s) "
"and file list or directory\n");
usage();
}
if ((string[0]=(char *)malloc(strlen(argv[optind])+1))==NULL){
fprintf(stderr,"tgrep: No space for search string(s)\n");
exit(1);
}
memset(string[0],0,strlen(argv[optind])+1);
strcpy(string[0],argv[optind]);
regexp_cnt=1;
optind++;
}
if (flags & FI_IGNCASE)
for (i=0; i<regexp_cnt; i++)
uncase(string[i]);
#ifdef __lock_lint
/*
** This is NOT somthing you really want to do. This
** function calls are here ONLY for warlock/locklint !!!
*/
pm_pat[i] = makepat(string[i],NULL);
bm_pat = bm_makepat(string[0]);
bm_freepat(bm_pat); /* stop it from becomming a root */
#else
if (flags & FE_REGEXP) {
for (i=0; i<regexp_cnt; i++)
pm_pat[i] = makepat(string[i],NULL);
use_pmatch = 1;
}
else {
bm_pat = bm_makepat(string[0]); /* only one allowed */
}
#endif
flags |= FX_STDIN;
max_open_files = sysconf(_SC_OPEN_MAX);
ncpus = sysconf(_SC_NPROCESSORS_ONLN);
if ((max_open_files - HOLD_FDS - debug_file) < 1) {
fprintf(stderr,"tgrep: You MUST have at lest ONE fd "
"that can be used, check limit (>10)\n");
exit(1);
}
search_thr_limit = max_open_files - HOLD_FDS - debug_file;
cascade_thr_limit = search_thr_limit / 2;
/* the number of files that can by open */
current_open_files = search_thr_limit;
mutex_init(&stat_lk,USYNC_THREAD,"stat");
mutex_init(&global_count_lk,USYNC_THREAD,"global_cnt");
mutex_init(&output_print_lk,USYNC_THREAD,"output_print");
mutex_init(&work_q_lk,USYNC_THREAD,"work_q");
mutex_init(&running_lk,USYNC_THREAD,"running");
cond_init(&work_q_cv,USYNC_THREAD,"work_q");
mutex_init(&search_q_lk,USYNC_THREAD,"search_q");
cond_init(&search_q_cv,USYNC_THREAD,"search_q");
mutex_init(&cascade_q_lk,USYNC_THREAD,"cascade_q");
cond_init(&cascade_q_cv,USYNC_THREAD,"cascade_q");
if ((argc == optind) && ((flags & TG_FILEPAT) || (flags & FR_RECUR))) {
add_work(".",DIRT);
flags = (flags & ~FX_STDIN);
}
for ( ; optind < argc; optind++) {
restart_cnt = 10;
flags = (flags & ~FX_STDIN);
STAT_AGAIN:
if (stat(argv[optind], &sbuf)) {
if (errno == EINTR) { /* try again !, restart */
if (--restart_cnt)
goto STAT_AGAIN;
26 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
}
if (!(flags & FS_NOERROR))
fprintf(stderr,"tgrep: Can't stat file/dir %s, %s\n",
argv[optind], strerror(errno));
continue;
}
switch (sbuf.st_mode & S_IFMT) {
case S_IFREG :
if (flags & TG_FILEPAT) {
if (pmatch(pm_file_pat, argv[optind], &pm_file_len))
add_work(argv[optind],FILET);
}
else {
add_work(argv[optind],FILET);
}
break;
case S_IFDIR :
if (flags & FR_RECUR) {
add_work(argv[optind],DIRT);
}
else {
if (!(flags & FS_NOERROR))
fprintf(stderr,"tgrep: Can't search directory %s, "
"-r option is on. Directory ignored.\n",
argv[optind]);
}
break;
}
}
NOTE(COMPETING_THREADS_NOW)
27 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
28 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
default:
fprintf(stderr,"tgrep: Internal error, work_t->tp no valid\n");
exit(1);
}
if (err) { /* NEED TO FIX THIS CODE. Exiting is just wrong */
fprintf(stderr,"Cound not create new thread!\n");
exit(1);
}
}
OUT:
if (flags & TG_PROGRESS) {
if (progress)
fprintf(stderr,".\n");
else
fprintf(stderr,"\n");
}
/* we are done, print the stuff. All other threads ar parked */
if (flags & FC_COUNT) {
mutex_lock(&global_count_lk);
printf("%d\n",global_count);
mutex_unlock(&global_count_lk);
}
if (flags & FS_STATS)
prnt_stats();
return(0); /* should have a return from main */
}
/*
* Add_Work: Called from the main thread, and cascade threads to add file
* and directory names to the work Q.
*/
int
add_work(char *path,int tp)
{
work_t
*wt,*ww,*wp;
if ((wt = (work_t *)malloc(sizeof(work_t))) == NULL)
goto ERROR;
if ((wt->path = (char *)malloc(strlen(path)+1)) == NULL)
goto ERROR;
strcpy(wt->path,path);
wt->tp = tp;
wt->next = NULL;
if (flags & FS_STATS) {
mutex_lock(&stat_lk);
if (wt->tp == DIRT)
st_dir_search++;
else
st_file_search++;
mutex_unlock(&stat_lk);
}
mutex_lock(&work_q_lk);
work_cnt++;
wt->next = work_q;
work_q = wt;
cond_signal(&work_q_cv);
mutex_unlock(&work_q_lk);
return(0);
ERROR:
if (!(flags & FS_NOERROR))
fprintf(stderr,"tgrep: Could not add %s to work queue. Ignored\n",
path);
return(-1);
}
/*
* Search thread: Started by the main thread when a file name is found
* on the work Q to be serached. If all the needed resources are ready
* a new search thread will be created.
*/
void *
search_thr(void *arg) /* work_t *arg */
{
FILE
*fin;
char
fin_buf[(BUFSIZ*4)]; /* 4 Kbytes */
work_t
*wt,std;
int
line_count;
char
rline[128];
char
cline[128];
char
*line;
29 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
30 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
**
**
**
**
*/
if
31 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
32 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
33 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
cascade_pool_cnt--;
wt = cascade_q; /* we have work to do! */
if (cascade_q->next)
cascade_q = cascade_q->next;
else
cascade_q = NULL;
mutex_unlock(&cascade_q_lk);
}
}
/*NOTREACHED*/
}
/*
* Print Local Output: Called by the search thread after it is done searching
* a single file. If any oputput was saved (matching lines), the lines are
* displayed as a group on stdout.
*/
int
print_local_output(out_t *out, work_t *wt)
{
out_t
*pp, *op;
int
out_count = 0;
int
printed = 0;
int
print_name = 1;
pp = out;
mutex_lock(&output_print_lk);
if (pp && (flags & TG_PROGRESS)) {
progress++;
if (progress >= progress_offset) {
progress = 0;
fprintf(stderr,".");
}
}
while (pp) {
out_count++;
if (!(flags & FC_COUNT)) {
if (flags & FL_NAMEONLY) { /* Pint name ONLY ! */
if (!printed) {
printed = 1;
printf("%s\n",wt->path);
}
}
else { /* We are printing more then just the name */
if (!(flags & FH_HOLDNAME)) /* do not print name ? */
printf("%s :",wt->path);
if (flags & FB_BLOCK)
printf("%ld:",pp->byte_count/512+1);
if (flags & FN_NUMBER)
printf("%d:",pp->line_count);
printf("%s\n",pp->line);
}
}
op = pp;
pp = pp->next;
/* free the nodes as we go down the list */
free(op->line);
free(op);
}
mutex_unlock(&output_print_lk);
mutex_lock(&global_count_lk);
global_count += out_count;
mutex_unlock(&global_count_lk);
return(0);
}
/*
* add output local: is called by a search thread as it finds matching lines.
* the matching line, it's byte offset, line count, etc are stored until the
* search thread is done searching the file, then the lines are printed as
* a group. This way the lines from more then a single file are not mixed
* together.
*/
int
add_output_local(out_t **out, work_t *wt,int lc, long bc, char *line)
{
out_t
*ot,*oo, *op;
if (( ot = (out_t *)malloc(sizeof(out_t))) == NULL)
goto ERROR;
if (( ot->line = (char *)malloc(strlen(line)+1)) == NULL)
goto ERROR;
34 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
strcpy(ot->line,line);
ot->line_count = lc;
ot->byte_count = bc;
if (!*out) {
*out = ot;
ot->next = NULL;
return(0);
}
/* append to the END of the list, keep things sorted! */
op = oo = *out;
while(oo) {
op = oo;
oo = oo->next;
}
op->next = ot;
ot->next = NULL;
return(0);
ERROR:
if (!(flags & FS_NOERROR))
fprintf(stderr,"tgrep: Output lost. No space. "
"[%s: line %d byte %d match : %s\n",
wt->path,lc,bc,line);
return(1);
}
/*
* print stats: If the -S flag is set, after ALL files have been searched,
* main thread calls this function to print the stats it keeps on how the
* search went.
*/
void
prnt_stats(void)
{
float a,b,c;
float t = 0.0;
time_t st_end = 0;
char
tl[80];
st_end = time(NULL); /* stop the clock */
fprintf(stderr,"\n----------------- Tgrep Stats. --------------------\n");
fprintf(stderr,"Number of directories searched:
%d\n",
st_dir_search);
fprintf(stderr,"Number of files searched:
%d\n",
st_file_search);
c = (float)(st_dir_search + st_file_search) / (float)(st_end - st_start);
fprintf(stderr,"Dir/files per second:
%3.2f\n",
c);
fprintf(stderr,"Number of lines searched:
%d\n",
st_line_search);
fprintf(stderr,"Number of matching lines to target:
%d\n",
global_count);
35 of 42
%d\n",
%d\n",
%d\n",
%d\n",
%3.2f%%\n",
%d\n",
%d\n",
%d\n",
%d\n",
%3.2f%%\n",
%d\n",
%d\n",
%d\n",
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
%d\n",
/*
* SigThread: if the -S option is set, the first ^C set to tgrep will
* print the stats on the fly, the second will kill the process.
*/
void *
SigThread(void *arg)
{
int sig;
int stats_printed = 0;
36 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
while (1) {
sig = sigwait(&set);
DP(DLEVEL7,("Signal %d caught\n",sig));
switch (sig) {
case -1:
fprintf(stderr,"Signal error\n");
break;
case SIGINT:
if (stats_printed)
exit(1);
stats_printed = 1;
sig_print_stats();
break;
case SIGHUP:
sig_print_stats();
break;
default:
DP(DLEVEL7,("Default action taken (exit) for signal %d\n",sig));
exit(1); /* default action */
}
}
}
void
sig_print_stats(void)
{
/*
** Get the output lock first
** Then get the stat lock.
*/
mutex_lock(&output_print_lk);
mutex_lock(&stat_lk);
prnt_stats();
mutex_unlock(&stat_lk);
mutex_unlock(&output_print_lk);
return;
}
/*
* usage: Have to have one of these.
*/
void
usage(void)
{
fprintf(stderr,"usage: tgrep <options> pattern <{file,dir}>...\n");
fprintf(stderr,"\n");
fprintf(stderr,"Where:\n");
#ifdef DEBUG
fprintf(stderr,"Debug
-d = debug level -d <levels> (-d0 for usage)\n");
fprintf(stderr,"Debug
-f = block fd's from use (-f #)\n");
#endif
fprintf(stderr,"
-b = show block count (512 byte block)\n");
fprintf(stderr,"
-c = print only a line count\n");
fprintf(stderr,"
-h = do not print file names\n");
fprintf(stderr,"
-i = case insensitive\n");
fprintf(stderr,"
-l = print file name only\n");
fprintf(stderr,"
-n = print the line number with the line\n");
fprintf(stderr,"
-s = Suppress error messages\n");
fprintf(stderr,"
-v = print all but matching lines\n");
#ifdef NOT_IMP
fprintf(stderr,"
-w = search for a \"word\"\n");
#endif
fprintf(stderr,"
-r = Do not search for files in all "
"sub-directories\n");
fprintf(stderr,"
-C = show continued lines (\"\\\")\n");
fprintf(stderr,"
-p = File name regexp pattern. (Quote it)\n");
fprintf(stderr,"
-P = show progress. -P 1 prints a DOT on stderr\n"
"
for each file it finds, -P 10 prints a DOT\n"
"
on stderr for each 10 files it finds, etc...\n");
fprintf(stderr,"
-e = expression search.(regexp) More then one\n");
fprintf(stderr,"
-B = limit the number of threads to TGLIMIT\n");
fprintf(stderr,"
-S = Print thread stats when done.\n");
fprintf(stderr,"
-Z = Print help on the regexp used.\n");
fprintf(stderr,"\n");
fprintf(stderr,"Notes:\n");
fprintf(stderr,"
If you start tgrep with only a directory name\n");
fprintf(stderr,"
and no file names, you must not have the -r option\n");
fprintf(stderr,"
set or you will get no output.\n");
fprintf(stderr,"
To search stdin (piped input), you must set -r\n");
fprintf(stderr,"
Tgrep will search ALL files in ALL \n");
fprintf(stderr,"
sub-directories. (like */* */*/* */*/*/* etc..)\n");
fprintf(stderr,"
if you supply a directory name.\n");
37 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
If you do not supply a file, or directory name,\n");
and the -r option is not set, the current \n");
directory \".\" will be used.\n");
All the other options should work \"like\" grep\n");
The -p patten is regexp, tgrep will search only\n");
the file names that match the patten\n");
Tgrep Version %s\n",Tgrep_Version);
Copy Right By Ron Winacott, 1993-1995.\n");
}
/*
* regexp usage: Tell the world about tgrep custom (THREAD SAFE) regexp!
*/
int
regexp_usage (void)
{
fprintf(stderr,"usage: tgrep <options> -e \"pattern\" <-e ...> "
"<{file,dir}>...\n");
fprintf(stderr,"\n");
fprintf(stderr,"metachars:\n");
fprintf(stderr,"
. - match any character\n");
fprintf(stderr,"
* - match 0 or more occurrences of pervious char\n");
fprintf(stderr,"
+ - match 1 or more occurrences of pervious char.\n");
fprintf(stderr,"
^ - match at begining of string\n");
fprintf(stderr,"
$ - match end of string\n");
fprintf(stderr,"
[ - start of character class\n");
fprintf(stderr,"
] - end of character class\n");
fprintf(stderr,"
( - start of a new pattern\n");
fprintf(stderr,"
) - end of a new pattern\n");
fprintf(stderr,"
@(n)c - match <c> at column <n>\n");
fprintf(stderr,"
| - match either pattern\n");
fprintf(stderr,"
\\ - escape any special characters\n");
fprintf(stderr,"
\\c - escape any special characters\n");
fprintf(stderr,"
\\o - turn on any special characters\n");
fprintf(stderr,"\n");
fprintf(stderr,"To match two diffrerent patterns in the same command\n");
fprintf(stderr,"Use the or function. \n"
"ie: tgrep -e \"(pat1)|(pat2)\" file\n"
"This will match any line with \"pat1\" or \"pat2\" in it.\n");
fprintf(stderr,"You can also use up to %d -e expresions\n",MAXREGEXP);
fprintf(stderr,"RegExp Pattern matching brought to you by Marc Staveley\n");
exit(0);
}
/*
* debug usage: If compiled with -DDEBUG, turn it on, and tell the world
* how to get tgrep to print debug info on different threads.
*/
#ifdef DEBUG
void
debug_usage(void)
{
int i = 0;
fprintf(stderr,"DEBUG usage and levels:\n");
fprintf(stderr,"--------------------------------------------------\n");
fprintf(stderr,"Level
code\n");
fprintf(stderr,"--------------------------------------------------\n");
fprintf(stderr,"0
This message.\n");
for (i=0; i<9; i++) {
fprintf(stderr,"%d
%s\n",i+1,debug_set[i].name);
}
fprintf(stderr,"--------------------------------------------------\n");
fprintf(stderr,"You can or the levels together like -d134 for levels\n");
fprintf(stderr,"1 and 3 and 4.\n");
fprintf(stderr,"\n");
exit(0);
}
#endif
Multithreaded Quicksort
The following example
tquick.cimplements
/*
* Multithreaded Demo Source
38 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
94043
/*
* multiple-thread quick-sort. See man page for qsort(3c) for info.
* Works fine on uniprocessor machines as well.
*
* Written by: Richard Pettit ([email protected])
*/
#include <unistd.h>
#include <stdlib.h>
#include <thread.h>
/* don't create more threads for less than this */
#define SLICE_THRESH
4096
/* how many threads per lwp */
#define THR_PER_LWP
4
/* cast the void to a one byte quanitity and compute the offset */
#define SUB(a, n)
((void *) (((unsigned char *) (a)) + ((n) * width)))
typedef struct {
void
*sa_base;
int
sa_nel;
size_t
sa_width;
int
(*sa_compar)(const void *, const void *);
} sort_args_t;
/* for all instances of quicksort */
static int threads_avail;
#define SWAP(a, i, j, width) \
{ \
int n; \
unsigned char uc; \
unsigned short us; \
unsigned long ul; \
unsigned long long ull; \
\
if (SUB(a, i) == pivot) \
pivot = SUB(a, j); \
else if (SUB(a, j) == pivot) \
pivot = SUB(a, i); \
\
/* one of the more convoluted swaps I've done */ \
switch(width) { \
case 1: \
uc = *((unsigned char *) SUB(a, i)); \
*((unsigned char *) SUB(a, i)) = *((unsigned char *) SUB(a, j)); \
*((unsigned char *) SUB(a, j)) = uc; \
break; \
case 2: \
us = *((unsigned short *) SUB(a, i)); \
39 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
40 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
41 of 42
11/10/2016 07:49 PM
https://users.cs.cf.ac.uk/Dave.Marshall/C/node3...
break;
case 3:
/* three sort */
if ((*compar)(SUB(a, i), SUB(a, i + 1)) > 0) {
SWAP(a, i, i + 1, width);
}
/* the first two are now ordered, now order the second two */
if ((*compar)(SUB(a, i + 2), SUB(a, i + 1)) < 0) {
SWAP(a, i + 2, i + 1, width);
}
/* should the second be moved to the first? */
if ((*compar)(SUB(a, i + 1), SUB(a, i)) < 0) {
SWAP(a, i + 1, i, width);
}
break;
default:
sort_args[1].sa_base
= SUB(a, i);
sort_args[1].sa_nel
= j;
sort_args[1].sa_width
= width;
sort_args[1].sa_compar
= compar;
if ((thread_count == 0) && (threads_avail > 0) && (i > SLICE_THRESH)) {
threads_avail--;
thr_create(0, 0, _quicksort, &sort_args[1], 0, &tid);
thread_count = 1;
} else
_quicksort(&sort_args[1]);
break;
}
if (thread_count) {
thr_join(tid, 0, 0);
threads_avail++;
}
return 0;
}
void
quicksort(void *a, size_t n, size_t width,
int (*compar)(const void *, const void *))
{
static int ncpus = -1;
sort_args_t sort_args;
if (ncpus == -1) {
ncpus = sysconf( _SC_NPROCESSORS_ONLN);
/* lwp for each cpu */
if ((ncpus > 1) && (thr_getconcurrency() < ncpus))
thr_setconcurrency(ncpus);
/* thread count not to exceed THR_PER_LWP per lwp */
threads_avail = (ncpus == 1) ? 0 : (ncpus * THR_PER_LWP);
}
sort_args.sa_base = a;
sort_args.sa_nel = n;
sort_args.sa_width = width;
sort_args.sa_compar = compar;
(void) _quicksort(&sort_args);
}
Dave Marshall
1/5/1999
42 of 42
11/10/2016 07:49 PM