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