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