1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * Communication between threads
5   *
6   * Copyright 1997, 2001 Bernd Schmidt
7   */
8 
9 typedef union {
10     int i;
11     uae_u32 u32;
12     void *pv;
13 } uae_pt;
14 
15 /* These currently require the maximum size to be known at initialization
16  * time, but it wouldn't be hard to use a "normal" pipe as an extension once the
17  * user-level one gets full.
18  * We queue up to chunks pieces of data before signalling the other thread to
19  * avoid overhead. */
20 
21 typedef struct {
22     uae_sem_t lock;
23     uae_sem_t reader_wait;
24     uae_sem_t writer_wait;
25     uae_pt *data;
26     int size, chunks;
27     volatile int rdp, wrp;
28     volatile int writer_waiting;
29     volatile int reader_waiting;
30 } smp_comm_pipe;
31 
init_comm_pipe(smp_comm_pipe * p,int size,int chunks)32 static __inline__ void init_comm_pipe (smp_comm_pipe *p, int size, int chunks)
33 {
34     p->data = (uae_pt *)malloc (size*sizeof (uae_pt));
35     p->size = size;
36     p->chunks = chunks;
37     p->rdp = p->wrp = 0;
38     p->reader_waiting = 0;
39     p->writer_waiting = 0;
40     uae_sem_init (&p->lock, 0, 1);
41     uae_sem_init (&p->reader_wait, 0, 0);
42     uae_sem_init (&p->writer_wait, 0, 0);
43 }
44 
destroy_comm_pipe(smp_comm_pipe * p)45 static __inline__ void destroy_comm_pipe (smp_comm_pipe *p)
46 {
47     uae_sem_destroy (&p->lock);
48     uae_sem_destroy (&p->reader_wait);
49     uae_sem_destroy (&p->writer_wait);
50 }
51 
maybe_wake_reader(smp_comm_pipe * p,int no_buffer)52 static __inline__ void maybe_wake_reader (smp_comm_pipe *p, int no_buffer)
53 {
54     if (p->reader_waiting
55 	&& (no_buffer || ((p->wrp - p->rdp + p->size) % p->size) >= p->chunks))
56     {
57 	p->reader_waiting = 0;
58 	uae_sem_post (&p->reader_wait);
59     }
60 }
61 
write_comm_pipe_pt(smp_comm_pipe * p,uae_pt data,int no_buffer)62 static __inline__ void write_comm_pipe_pt (smp_comm_pipe *p, uae_pt data, int no_buffer)
63 {
64     int nxwrp = (p->wrp + 1) % p->size;
65 
66     if (p->reader_waiting) {
67 	/* No need to do all the locking */
68 	p->data[p->wrp] = data;
69 	p->wrp = nxwrp;
70 	maybe_wake_reader (p, no_buffer);
71 	return;
72     }
73 
74     uae_sem_wait (&p->lock);
75     if (nxwrp == p->rdp) {
76 	/* Pipe full! */
77 	p->writer_waiting = 1;
78 	uae_sem_post (&p->lock);
79 	/* Note that the reader could get in between here and do a
80 	 * sem_post on writer_wait before we wait on it. That's harmless.
81 	 * There's a similar case in read_comm_pipe_int_blocking. */
82 	uae_sem_wait (&p->writer_wait);
83 	uae_sem_wait (&p->lock);
84     }
85     p->data[p->wrp] = data;
86     p->wrp = nxwrp;
87     maybe_wake_reader (p, no_buffer);
88     uae_sem_post (&p->lock);
89 }
90 
read_comm_pipe_pt_blocking(smp_comm_pipe * p)91 static __inline__ uae_pt read_comm_pipe_pt_blocking (smp_comm_pipe *p)
92 {
93     uae_pt data;
94 
95     uae_sem_wait (&p->lock);
96     if (p->rdp == p->wrp) {
97 	p->reader_waiting = 1;
98 	uae_sem_post (&p->lock);
99 	uae_sem_wait (&p->reader_wait);
100 	uae_sem_wait (&p->lock);
101     }
102     data = p->data[p->rdp];
103     p->rdp = (p->rdp + 1) % p->size;
104 
105     /* We ignore chunks here. If this is a problem, make the size bigger in the init call. */
106     if (p->writer_waiting) {
107 	p->writer_waiting = 0;
108 	uae_sem_post (&p->writer_wait);
109     }
110     uae_sem_post (&p->lock);
111     return data;
112 }
113 
comm_pipe_has_data(smp_comm_pipe * p)114 static __inline__ int comm_pipe_has_data (smp_comm_pipe *p)
115 {
116     return p->rdp != p->wrp;
117 }
118 
read_comm_pipe_int_blocking(smp_comm_pipe * p)119 static __inline__ int read_comm_pipe_int_blocking (smp_comm_pipe *p)
120 {
121     uae_pt foo = read_comm_pipe_pt_blocking (p);
122     return foo.i;
123 }
read_comm_pipe_u32_blocking(smp_comm_pipe * p)124 static __inline__ uae_u32 read_comm_pipe_u32_blocking (smp_comm_pipe *p)
125 {
126     uae_pt foo = read_comm_pipe_pt_blocking (p);
127     return foo.u32;
128 }
129 
read_comm_pipe_pvoid_blocking(smp_comm_pipe * p)130 static __inline__ void *read_comm_pipe_pvoid_blocking (smp_comm_pipe *p)
131 {
132     uae_pt foo = read_comm_pipe_pt_blocking (p);
133     return foo.pv;
134 }
135 
write_comm_pipe_int(smp_comm_pipe * p,int data,int no_buffer)136 static __inline__ void write_comm_pipe_int (smp_comm_pipe *p, int data, int no_buffer)
137 {
138     uae_pt foo;
139     foo.i = data;
140     write_comm_pipe_pt (p, foo, no_buffer);
141 }
142 
write_comm_pipe_u32(smp_comm_pipe * p,int data,int no_buffer)143 static __inline__ void write_comm_pipe_u32 (smp_comm_pipe *p, int data, int no_buffer)
144 {
145     uae_pt foo;
146     foo.u32 = data;
147     write_comm_pipe_pt (p, foo, no_buffer);
148 }
149 
write_comm_pipe_pvoid(smp_comm_pipe * p,void * data,int no_buffer)150 static __inline__ void write_comm_pipe_pvoid (smp_comm_pipe *p, void *data, int no_buffer)
151 {
152     uae_pt foo;
153     foo.pv = data;
154     write_comm_pipe_pt (p, foo, no_buffer);
155 }
156