I am working on a multithreaded app. It crashes occasionally and I not completely sure why. It seems to happen when I make an rpc call.
Are the rpc and dma modules thread safe? Can two threads make an rpc call or dma at the same time? And even if they do I assume they get queued up at the iop end.
threading issues
I found the issue, it appears that rpc calls are not thread safe, at least not if they share the same t_SifRpcClientData . Not sure about dma but I haven't any issues with it yet.
Here are the changes:
Here are the changes:
Code: Select all
Index: audsrv/include/audsrv.h
===================================================================
--- audsrv/include/audsrv.h (revision 1232)
+++ audsrv/include/audsrv.h (working copy)
@@ -43,6 +43,7 @@
#define AUDSRV_ERR_NO_MORE_CHANNELS 0x0007
#define AUDSRV_ERR_FAILED_TO_LOAD_ADPCM 0x0010
+#define AUDSRV_ERR_FAILED_TO_CREATE_SEMA 0x0011
/** structure used to set new format */
typedef struct audsrv_fmt_t
Index: audsrv/src/audsrv_rpc.c
===================================================================
--- audsrv/src/audsrv_rpc.c (revision 1232)
+++ audsrv/src/audsrv_rpc.c (working copy)
@@ -35,6 +35,7 @@
static int initialized = 0;
static int audsrv_error = AUDSRV_ERR_NOERROR;
+static int completion_sema;
/* nasty forwards */
static void fillbuf_requested(void *pkt, void *arg);
@@ -48,6 +49,11 @@
audsrv_error = err;
}
+static void _audsrv_intr()
+{
+ iSignalSema(completion_sema);
+}
+
/** Returns the last error audsrv raised
@returns error code
*/
@@ -63,8 +69,10 @@
*/
static int call_rpc_1(int func, int arg)
{
+ WaitSema(completion_sema);
+
sbuff[0] = arg;
- SifCallRpc(&cd0, func, 0, sbuff, 1*4, sbuff, 4, 0, 0);
+ SifCallRpc(&cd0, func, 0, sbuff, 1*4, sbuff, 4, _audsrv_intr, 0);
FlushCache(0);
set_error(sbuff[0]);
@@ -79,9 +87,11 @@
*/
static int call_rpc_2(int func, int arg1, int arg2)
{
+ WaitSema(completion_sema);
+
sbuff[0] = arg1;
sbuff[1] = arg2;
- SifCallRpc(&cd0, func, 0, sbuff, 2*4, sbuff, 4, 0, 0);
+ SifCallRpc(&cd0, func, 0, sbuff, 2*4, sbuff, 4, _audsrv_intr, 0);
FlushCache(0);
set_error(sbuff[0]);
@@ -93,7 +103,9 @@
*/
int audsrv_quit()
{
- SifCallRpc(&cd0, AUDSRV_QUIT, 0, sbuff, 1*4, sbuff, 4, 0, 0);
+ WaitSema(completion_sema);
+
+ SifCallRpc(&cd0, AUDSRV_QUIT, 0, sbuff, 1*4, sbuff, 4, _audsrv_intr, 0);
set_error(AUDSRV_ERR_NOERROR);
return 0;
}
@@ -109,10 +121,12 @@
*/
int audsrv_set_format(struct audsrv_fmt_t *fmt)
{
+ WaitSema(completion_sema);
+
sbuff[0] = fmt->freq;
sbuff[1] = fmt->bits;
sbuff[2] = fmt->channels;
- SifCallRpc(&cd0, AUDSRV_SET_FORMAT, 0, sbuff, 3*4, sbuff, 4, 0, 0);
+ SifCallRpc(&cd0, AUDSRV_SET_FORMAT, 0, sbuff, 3*4, sbuff, 4, _audsrv_intr, 0);
FlushCache(0);
set_error(sbuff[0]);
return sbuff[0];
@@ -268,11 +282,13 @@
maxcopy = sizeof(sbuff) - sizeof(int);
while (bytes > 0)
{
+ WaitSema(completion_sema);
+
copy = MIN(bytes, maxcopy);
sbuff[0] = copy;
memcpy(&sbuff[1], chunk, copy);
packet_size = copy + sizeof(int);
- SifCallRpc(&cd0, AUDSRV_PLAY_AUDIO, 0, sbuff, packet_size, sbuff, 1*4, 0, 0);
+ SifCallRpc(&cd0, AUDSRV_PLAY_AUDIO, 0, sbuff, packet_size, sbuff, 1*4, _audsrv_intr, 0);
FlushCache(0);
if (sbuff[0] < 0)
@@ -331,6 +347,12 @@
nopdelay();
}
+ ee_sema_t compSema;
+ compSema.init_count = 1;
+ compSema.max_count = 1;
+ compSema.option = 0;
+ completion_sema = CreateSema(&compSema);
+
SifCallRpc(&cd0, AUDSRV_INIT, 0, sbuff, 64, sbuff, 64, 0, 0);
FlushCache(0);
ret = sbuff[0];
@@ -374,6 +396,8 @@
SifDmaTransfer_t sifdma;
int id;
+ WaitSema(completion_sema);
+
iop_addr = SifAllocIopHeap(size);
if (iop_addr == 0)
{
@@ -393,7 +417,7 @@
sbuff[1] = size;
sbuff[2] = (int)adpcm; /* use as id */
- SifCallRpc(&cd0, AUDSRV_LOAD_ADPCM, 0, sbuff, 12, sbuff, 16, 0, 0);
+ SifCallRpc(&cd0, AUDSRV_LOAD_ADPCM, 0, sbuff, 12, sbuff, 16, _audsrv_intr, 0);
SifFreeIopHeap(iop_addr);
if(sbuff[0] != 0)
There is only one synchronisation object on the PS2, they call it a sema. It is a basic atomic increment/decrement object with a wait state when it reaches zero.apache37 wrote:Is there a good reference where I can learn about threads/semaphores on ps2? I would like to know if variables can be protected or locked as well as joining threads.
You can use it like a traditional simple mutex. Simple meaning it doesn't support recursive locking.
You can use it like a traditional simple semaphore. Simple meaning only one waiting thread will wake up. Also if no thread is currently waiting then the next thread to wait will wake up immediately.
You can also use it build up more complex mutexes. One which support recursive locking.
Or to buid up complex semaphores. Ones which can wake all waiting threads. And only wake currently waiting threads.
Lastly there is no direct support for thread joining. You can build your own from this basic sema though. Just get the thread to signal the sema when it exits.