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