1 /* Copyright (C) 2006  Britton Leo Kerin, see copyright. */
2 
3 /* This function does the actual moving of the audio data between the
4    ring buffer and the arg_fd disk data file.  This function should
5    only be executed as a thread.  */
6 
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <sys/types.h>
13 
14 #include "rawrec.h"
15 #include "thread_functions.h"
16 
17 /* Junk move_au uses to let the main thread (which gets to do signal
18    handling) know that we have finished.  See comments in play.c for a
19    fuller explanation of how this works.  Needs to be replaced when
20    kernel/glibc thread support gets better. */
21 extern pthread_mutex_t tell_main_follower_done_mutex;
22 extern int tell_main_follower_done;
23 
move_fd(move_fd_th_arg_stt * th_arg)24 void * move_fd(move_fd_th_arg_stt *th_arg)
25 {
26   double bytes_done = 0;       /* Number of bytes already moved. */
27   /* This flag is true if this is our first time around the ring buffer.  */
28   int first_time_around = 1;
29 
30   /* This global should have been set before any threads were spawned,
31      and should never get changed.  */
32   if ( have_root_authority ) {
33     /* We want to drop root permissions as soon as all the threads
34        have been created.  But effective root permissions are a
35        process wide attribute (rather than a per thread attribute like
36        in the old linux threads implementation) so if we drop them
37        immediately on thread entry, we might not be able to create the
38        second data thread (move_au or move_fd, depending on startup
39        order).  So we wait here for the thread doing the spawning to
40        set a condition variable indicating that root permissions have
41        been dropped before going on.  */
42     pthread_mutex_lock(&root_permissions_dropped_mutex);
43     while ( !root_permissions_dropped ) {
44       /* This loop guards against spurious wakeups, which according to
45 	 pthreads standard are possible.  */
46       pthread_cond_wait(&root_permissions_dropped_cv,
47 			&root_permissions_dropped_mutex);
48     }
49     pthread_mutex_unlock (&root_permissions_dropped_mutex);
50   }
51 
52   /* If this thread has startup priority of 1, grab the first buffer
53      segment and signal the move_au_th when done. */
54   if ( th_arg->startup_order == 1 ) {
55     pthread_mutex_lock(&seg_mutex[0]);
56     /* Signal the startup_next condition variable.  Mutex needs attrs
57        still. */
58     pthread_mutex_lock(&startup_next_mutex);
59     startup_next = 2;
60     pthread_cond_signal(&startup_next_cv);
61     pthread_mutex_unlock(&startup_next_mutex);
62   } else {
63     /* In this program there are only two threads trying to access the
64        ring buffer, and this else clause handles the case where this
65        thread is not the first to get the buffer (where this thread
66        has startup_order == 2). */
67     pthread_mutex_lock(&startup_next_mutex);
68     while ( startup_next != 2 )
69       /* This loop guards against spurious wakeups.  In this
70          application there is no third thread to slip in and change
71          startup_next, and indeed if this value is being used as
72          intended no slip in should ever occur. */
73       pthread_cond_wait(&startup_next_cv, &startup_next_mutex);
74     /* Grab what we've been waiting for clearance to grab. */
75     pthread_mutex_lock(&seg_mutex[0]);
76     pthread_mutex_unlock(&startup_next_mutex);
77     /* In this case, we're done with startup_next_cv and friends. */
78     pthread_cond_destroy(&startup_next_cv);
79     pthread_mutexattr_destroy(&startup_next_mutex_attr);
80     pthread_mutex_destroy(&startup_next_mutex);
81   }
82 
83   if ( th_arg->recorder ) {
84     int bytes_to_write;		/* # of bytes to write with next write() */
85     int bytes_written;		/* number of bytes written by write syscall */
86     int crnt_seg = 0;		/* current ring buffer segment index */
87 
88     /* Perform thread duties. */
89     for ( bytes_done = 0 ; bytes_done < th_arg->byte_cnt ;
90                bytes_done += bytes_written ) {
91       /* Note that this catches and reports on the broken pipe condition. */
92       bytes_to_write = (int) min(th_arg->seg_sz, th_arg->byte_cnt
93 				                 - bytes_done);
94       if ( (bytes_written = write(th_arg->fd, ringbufp + th_arg->seg_sz
95                * crnt_seg, (size_t) bytes_to_write)) == -1 ) {
96 	err_die("write syscall to output failed: %s\n", strerror(errno));
97       }
98       if ( bytes_written < bytes_to_write ) {
99 	err_die("write syscall wrote less than expected to output, "
100 		"giving up\n");
101       }
102 
103       /* If this segment was marked as the last by the move_au thread,
104 	 break out of the record loop.  */
105       if ( is_last_seg[crnt_seg] )
106 	break;
107 
108       pthread_mutex_lock(&seg_mutex[(crnt_seg + 1) % th_arg->ringbuf_segs]);
109       pthread_mutex_unlock(&seg_mutex[crnt_seg]);
110       crnt_seg = (crnt_seg + 1) % th_arg->ringbuf_segs;
111     }
112     /* Unlock the last segment locked by the above for loop. */
113     pthread_mutex_unlock(&seg_mutex[crnt_seg]);
114 
115     /* start signal handling hackery */
116     /* We are recording, so move_fd is the following thread.  Let the
117        main thread know that we are done.  */
118     pthread_mutex_lock(&tell_main_follower_done_mutex);
119     tell_main_follower_done = 1;
120     pthread_mutex_unlock(&tell_main_follower_done_mutex);
121     /* end signal handling hackery */
122 
123   } else {  /* If not record, then play. */
124     int bytes_to_read;		   /* # of bytes to read with next read() */
125     int bytes_read;		   /* holds return of read() syscalls */
126     int crnt_seg = 0;		   /* current ring buffer segment index */
127     int empty_seg_seq_length = 0;  /* number of empty segments in a row */
128 
129     /* This for loop is similar to the above except it reads data from
130        the file descriptor, rather then writing to it, so we have to
131        worry about pipeline undersupply (instead of just assuming a
132        terminal disaster if we read less than we wanted).  */
133     for ( bytes_done = 0 ; bytes_done < th_arg->byte_cnt ;
134                bytes_done += bytes_read ) {
135       bytes_to_read = min(th_arg->seg_sz, th_arg->byte_cnt - bytes_done);
136       if ( (bytes_read = read(th_arg->fd, ringbufp + th_arg->seg_sz
137                * crnt_seg, (size_t) bytes_to_read)) == -1 ) {
138 	err_die("read of input file descriptor failed: %s\n", strerror(errno));
139       }
140 
141       /* If we were reading from a file and not a pipeline, and we got
142          less than we asked for, give up (if we are using standard io,
143          the case where we read many empty segments is handled by
144          move_au, so that we don't quit prematurely if there is a
145          bunch of good data in the ring buffer waiting to be played). */
146       if ( (th_arg->using_stdio == FALSE)
147                && (bytes_read < bytes_to_read) ) {
148 	err_die("read syscall read less than expected from argument file, "
149 		"giving up\n");
150       }
151       /* keep track of the number of empty segments in a row */
152       if ( bytes_read == 0 )
153 	empty_seg_seq_length++;
154       else			/* bytes_read != 0 */
155 	empty_seg_seq_length = 0;
156       /* tell the move_au thread how many bytes got read into crnt_seg */
157       bytes_in_seg[crnt_seg] = bytes_read;
158 
159       /* Before we wrap back around to the start of the ring buffer
160          for the first time, we need to make sure that the move_au
161          thread has had a chance to at least get started and grab the
162          first buffer segment. */
163       if ( (first_time_around) && (crnt_seg == th_arg->ringbuf_segs - 1) ) {
164 	pthread_mutex_lock(&wrap_ready_mutex);
165 	while ( !wrap_ready )
166 	  /* This while loop guards against spurious wakeups.  */
167 	  pthread_cond_wait(&wrap_ready_cv, &wrap_ready_mutex);
168 	pthread_mutex_unlock(&wrap_ready_mutex);
169 	first_time_around = 0;	/* No longer our first time around.  */
170 	/* We are now done with wrap_ready_cv and friends.  */
171 	pthread_cond_destroy(&wrap_ready_cv);
172 	pthread_mutex_destroy(&wrap_ready_mutex);
173       }
174 
175       /* If we have seen a signal which is to cause program exit,
176          exit.  In this play context, move_fd leads move_au, but we
177          don't want to wait for everything move_fd may have loaded
178          into the buffer to play, so there is no need to notify
179          move_au of the point where we quit, it will just quit as soon
180          as it can anyway.  */
181       pthread_mutex_lock(&shutdown_signal_seen_mutex);
182       if ( shutdown_signal_seen ) {
183 	/* unlock the mutex to be sure move_au gets a chance to check it.  */
184 	pthread_mutex_unlock(&shutdown_signal_seen_mutex);
185 	break;  /* out of play loop.  */
186       }
187       pthread_mutex_unlock(&shutdown_signal_seen_mutex);
188 
189       if ( empty_seg_seq_length >= MAGIC_EMPTY_SEG_SEQ_LENGTH )
190 	/* This thread is done, move_au will worry about this
191            condition when it catches up. */
192 	break;			/* out of play loop */
193 
194       /* Perform staggered locking, and update crnt_seg.  */
195       pthread_mutex_lock(&seg_mutex[(crnt_seg + 1) % th_arg->ringbuf_segs]);
196       pthread_mutex_unlock(&seg_mutex[crnt_seg]);
197       crnt_seg = (crnt_seg + 1) % th_arg->ringbuf_segs;
198     }
199     /* Unlock the last segment locked by the above for loop. */
200     pthread_mutex_unlock(&seg_mutex[crnt_seg]);
201   }
202 
203   return ( (void *) &th_success);
204 }
205