1 #include "include/ioman.h"
11 #define MAX_IO_REQUESTS 64
12 #define MAX_IO_HANDLERS 64
16 static int gIOTerminate
= 0;
18 #define THREAD_STACK_SIZE (8 * 1024)
20 static u8 disp_stack
[THREAD_STACK_SIZE
] ALIGNED(16);
21 static u8 thread_stack
[THREAD_STACK_SIZE
] ALIGNED(16);
26 struct io_request_t
*next
;
31 io_request_handler_t handler
;
34 /// Circular request queue
35 static struct io_request_t
*gReqList
;
36 static struct io_request_t
*gReqEnd
;
38 static struct io_handler_t gRequestHandlers
[MAX_IO_HANDLERS
];
41 static void ioThreadDispatcher ( void* apParam
);
42 static void ioAlarmFunc(s32 id
, u16 time
, void *arg
);
44 static int gHandlerCount
;
46 // id of the processing thread
47 static s32 gIOThreadId
;
48 // id of the thread that handles thread switching
49 static s32 gDispatcherThreadID
;
51 // lock for tip processing
52 static s32 gProcSemaId
;
54 static s32 gEndSemaId
;
56 static s32 gIOPrintfSemaId
;
58 static ee_thread_t gIOThread
;
59 static ee_sema_t gQueueSema
;
61 static int isIOBlocked
= 0;
62 static int isIORunning
= 0;
63 static int alarmID
= 0;
64 static int stopIOTimer
= 0;
66 int ioRegisterHandler(int type
, io_request_handler_t handler
) {
67 WaitSema(gProcSemaId
);
70 return IO_ERR_INVALID_HANDLER
;
72 if (gHandlerCount
>= MAX_IO_HANDLERS
)
73 return IO_ERR_TOO_MANY_HANDLERS
;
77 for (i
= 0; i
< gHandlerCount
; ++i
) {
78 if (gRequestHandlers
[i
].type
== type
)
79 return IO_ERR_DUPLICIT_HANDLER
;
82 gRequestHandlers
[gHandlerCount
].type
= type
;
83 gRequestHandlers
[gHandlerCount
].handler
= handler
;
86 SignalSema(gProcSemaId
);
91 static io_request_handler_t
ioGetHandler(int type
) {
94 for (i
= 0; i
< gHandlerCount
; ++i
) {
95 struct io_handler_t
* h
= &gRequestHandlers
[i
];
104 static void ioProcessRequest(struct io_request_t
* req
) {
108 io_request_handler_t hlr
= ioGetHandler(req
->type
);
110 // invalidate the request
111 void *data
= req
->data
;
117 static void ioWorkerThread(void) {
118 while (!gIOTerminate
) {
121 // no processing when io is blocked
125 // if term requested exit immediately from the loop
129 // do we have a request in the queue?
131 WaitSema(gProcSemaId
);
133 struct io_request_t
* req
= gReqList
;
134 ioProcessRequest(req
);
136 // lock the queue tip as well now
137 WaitSema(gEndSemaId
);
139 // can't be sure if the request was
140 gReqList
= req
->next
;
146 SignalSema(gProcSemaId
);
147 SignalSema(gEndSemaId
);
151 // delete the pending requests
153 struct io_request_t
* req
= gReqList
;
154 gReqList
= gReqList
->next
;
155 free(req
); // TODO: Leak over here - we need a propper flag to free/not the user data
158 // delete the semaphores
159 DeleteSema(gProcSemaId
);
160 DeleteSema(gEndSemaId
);
167 static void ioSimpleActionHandler(void *data
) {
168 io_simpleaction_t action
= (io_simpleaction_t
)data
;
180 gDispatcherThreadID
= 0;
183 gQueueSema
.init_count
= 1;
184 gQueueSema
.max_count
= 1;
185 gQueueSema
.option
= 0;
187 gProcSemaId
= CreateSema(&gQueueSema
);
188 gEndSemaId
= CreateSema(&gQueueSema
);
189 gIOPrintfSemaId
= CreateSema(&gQueueSema
);
191 ChangeThreadPriority ( GetThreadId (), 29 );
193 // default custom simple action handler
194 ioRegisterHandler(IO_CUSTOM_SIMPLEACTION
, &ioSimpleActionHandler
);
196 memset(&gIOThread
, 0, sizeof(gIOThread
));
198 gIOThread
.func
= (void *)ioThreadDispatcher
;
199 gIOThread
.stack
= disp_stack
;
200 gIOThread
.stack_size
= THREAD_STACK_SIZE
;
201 gIOThread
.gp_reg
= &_gp
;
202 gIOThread
.initial_priority
= 0;
204 alarmID
= SetAlarm( 625, &ioAlarmFunc
, NULL
);
206 // this one manages the thread switching in alarm intervals
207 gDispatcherThreadID
= CreateThread(&gIOThread
);
208 StartThread(gDispatcherThreadID
, NULL
);
210 gIOThread
.func
= (void *)ioWorkerThread
;
211 gIOThread
.stack
= thread_stack
;
212 gIOThread
.initial_priority
= 30;
215 gIOThreadId
= CreateThread(&gIOThread
);
216 StartThread(gIOThreadId
, NULL
);
218 ChangeThreadPriority ( GetThreadId (), 30 );
221 int ioPutRequest(int type
, void* data
) {
223 return IO_ERR_IO_BLOCKED
;
225 // check the type before queueing
226 if (!ioGetHandler(type
))
227 return IO_ERR_INVALID_HANDLER
;
229 WaitSema(gEndSemaId
);
231 // We don't have to lock the tip of the queue...
232 // If it exists, it won't be touched, if it does not exist, it is not being processed
233 struct io_request_t
* req
= gReqEnd
;
236 gReqList
= (struct io_request_t
*)malloc(sizeof(struct io_request_t
));
240 req
->next
= (struct io_request_t
*)malloc(sizeof(struct io_request_t
));
249 SignalSema(gEndSemaId
);
251 WakeupThread(gIOThreadId
);
255 int ioRemoveRequests(int type
) {
256 // TODO: This one needs free flag to stop leaks
257 // lock the deletion sema and the queue end sema as well
258 WaitSema(gEndSemaId
);
259 WaitSema(gProcSemaId
);
262 struct io_request_t
* req
= gReqList
;
263 struct io_request_t
* last
= NULL
;
266 if (req
->type
== type
) {
267 struct io_request_t
* next
= req
->next
;
288 SignalSema(gProcSemaId
);
289 SignalSema(gEndSemaId
);
295 // termination requested flag
298 // wake up and wait for end
299 WakeupThread(gIOThreadId
);
300 WakeupThread(gDispatcherThreadID
);
302 // Cancel the thread dispatcher alarm (TODO: Is this correct?)
303 ReleaseAlarm(alarmID
);
306 int ioGetPendingRequestCount(void) {
309 struct io_request_t
* req
= gReqList
;
312 count
++; req
= req
->next
;
318 int ioHasPendingRequests(void) {
319 return gReqList
!= NULL
? 1 : 0;
323 // From PS2DEV.ORG - Thread dispatcher
324 static void ioThreadDispatcher ( void* apParam
) {
325 while ( ! gIOTerminate
) {
330 } /* end dispatcher */
332 static void ioAlarmFunc(s32 id
, u16 time
, void *arg
) {
333 if (!gIOTerminate
&& !stopIOTimer
) {
334 iWakeupThread ( gDispatcherThreadID
);
335 iRotateThreadReadyQueue ( 30 );
336 alarmID
= iSetAlarm ( 625, &ioAlarmFunc
, NULL
);
341 static char tbuf
[2048];
344 int ioPrintf(const char* format
, ...) {
345 WaitSema(gIOPrintfSemaId
);
348 va_start(args
, format
);
350 int ret
= vsnprintf((char *)tbuf
, sizeof(tbuf
), format
, args
);
353 int ret
= vprintf(format
, args
);
357 SignalSema(gIOPrintfSemaId
);
361 int ioBlockOps(int block
) {
362 if (block
&& !isIOBlocked
) {
365 // wait for all io to finish
366 while (ioHasPendingRequests()) {
367 // TODO: This is in seconds, so can be too coarse
371 // now all io should be blocked
372 // stop the timer as well...
374 ReleaseAlarm(alarmID
);
375 } else if (!block
&& isIOBlocked
) {
378 // create the alarm again
379 alarmID
= SetAlarm( 625, &ioAlarmFunc
, NULL
);