diff --git a/amiga/gui.c b/amiga/gui.c index e76a01e66..461a20dab 100644 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -167,6 +167,7 @@ struct ami_gui_tb_userdata { int items; }; +static struct MsgPort *schedulermsgport = NULL; static struct MsgPort *appport; static Class *urlStringClass; @@ -416,7 +417,9 @@ static bool ami_open_resources(void) ASO_NoTrack, FALSE, TAG_DONE))) return false; - ami_file_req_init(); + if(!(schedulermsgport = AllocSysObjectTags(ASOT_PORT, + ASO_NoTrack, FALSE, + TAG_DONE))) return false; return true; } @@ -2689,12 +2692,11 @@ void ami_get_msg(void) { ULONG winsignal = 1L << sport->mp_SigBit; ULONG appsig = 1L << appport->mp_SigBit; - ULONG schedulesig = 1L << msgport->mp_SigBit; + ULONG schedulesig = 1L << schedulermsgport->mp_SigBit; ULONG ctrlcsig = SIGBREAKF_CTRL_C; uint32 signal = 0; fd_set read_fd_set, write_fd_set, except_fd_set; int max_fd = -1; - struct TimerRequest *timermsg = NULL; struct MsgPort *printmsgport = ami_print_get_msgport(); ULONG printsig = 0; ULONG helpsignal = ami_help_signal(); @@ -2743,10 +2745,7 @@ void ami_get_msg(void) } if(signal & schedulesig) { - if((timermsg = (struct TimerRequest *)GetMsg(msgport))) { - ReplyMsg((struct Message *)timermsg); - schedule_run(); - } + ami_schedule_handle(schedulermsgport); } if(signal & helpsignal) @@ -2994,17 +2993,17 @@ static void gui_quit(void) ami_mouse_pointers_free(); LOG(("Freeing clipboard")); ami_clipboard_free(); + LOG(("Removing scheduler process")); + ami_scheduler_process_delete(); - FreeSysObject(ASOT_PORT,appport); - FreeSysObject(ASOT_PORT,sport); + FreeSysObject(ASOT_PORT, appport); + FreeSysObject(ASOT_PORT, sport); + FreeSysObject(ASOT_PORT, schedulermsgport); ami_file_req_free(); ami_openurl_close(); FreeStringClass(urlStringClass); - LOG(("Freeing scheduler")); - ami_schedule_free(); - FreeObjList(window_list); FreeVec(current_user_options); @@ -5348,6 +5347,16 @@ int main(int argc, char** argv) CloseLibrary(PopupMenuBase); } + if (ami_open_resources() == false) { /* alloc message ports */ + ami_misc_fatal_error("Unable to allocate resources"); + return RETURN_FAIL; + } + + if(ami_scheduler_process_create(schedulermsgport) != NSERROR_OK) { + ami_misc_fatal_error("Failed to initialise scheduler"); + return RETURN_FAIL; + } + user = GetVar("user", temp, 1024, GVF_GLOBAL_ONLY); current_user = ASPrintf("%s", (user == -1) ? "Default" : temp); current_user_dir = ASPrintf("PROGDIR:Users/%s", current_user); @@ -5365,11 +5374,6 @@ int main(int argc, char** argv) ami_mime_init("PROGDIR:Resources/mimetypes"); sprintf(temp, "%s/mimetypes.user", current_user_dir); ami_mime_init(temp); - if(ami_schedule_create() == false) { - ami_misc_fatal_error("Failed to initialise scheduler"); - return RETURN_FAIL; - } - amiga_plugin_hack_init(); ret = amiga_datatypes_init(); @@ -5396,11 +5400,6 @@ int main(int argc, char** argv) if(current_user_cache != NULL) FreeVec(current_user_cache); ret = amiga_icon_init(); - if (ami_open_resources() == false) { /* alloc ports/asl reqs, open libraries/devices */ - ami_misc_fatal_error("Unable to allocate resources"); - return RETURN_FAIL; - } - search_web_init(nsoption_charp(search_engines_file)); ami_clipboard_init(); ami_openurl_open(); @@ -5410,6 +5409,7 @@ int main(int argc, char** argv) save_complete_init(); ami_theme_init(); ami_init_mouse_pointers(); + ami_file_req_init(); win_destroyed = false; ami_font_setdevicedpi(0); /* for early font requests, eg treeview init */ diff --git a/amiga/schedule.c b/amiga/schedule.c index 6f2037d7e..607c3d8e5 100755 --- a/amiga/schedule.c +++ b/amiga/schedule.c @@ -1,5 +1,5 @@ /* - * Copyright 2008, 2011 Chris Young + * Copyright 2008 - 2014 Chris Young * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -18,6 +18,7 @@ #include "amiga/os3support.h" +#include #include #include @@ -26,9 +27,15 @@ #include #include "utils/errors.h" +#include "utils/log.h" #include "amiga/schedule.h" +static struct MsgPort *smsgport = NULL; /* to send messages for the scheduler to */ +static struct TimeRequest *tioreq; +struct Device *TimerBase; +struct TimerIFace *ITimer; + struct nscallback { struct TimeVal tv; @@ -37,6 +44,21 @@ struct nscallback struct TimeRequest *treq; }; +struct ami_schedule_message { + struct Message msg; + int type; + int t; + void *callback; + void *p; +}; + +enum { + AMI_S_SCHEDULE = 0, + AMI_S_RUN, + AMI_S_STARTUP, + AMI_S_EXIT +}; + static PblHeap *schedule_list; /** @@ -201,13 +223,35 @@ static int ami_schedule_compare(const void *prev, const void *next) } -/* exported function documented in amiga/schedule.h */ -void schedule_run(void) +/** + * Process events up to current time. + * NetSurf entry point after being signalled by the scheduler process. + */ +static void schedule_run(struct ami_schedule_message *asmsg) +{ + void (*callback)(void *p) = asmsg->callback; + void *p = asmsg->p; + + callback(p); +} + +/** + * Process events up to current time. + * + * This implementation only takes the top entry off the heap, it does not + * venture to later scheduled events until the next time it is called - + * immediately afterwards, if we're in a timer signalled loop. + */ +static void ami_scheduler_run(struct MsgPort *nsmsgport) { struct nscallback *nscb; void (*callback)(void *p); void *p; + struct ami_schedule_message *asmsg = AllocSysObjectTags(ASOT_MESSAGE, + ASOMSG_Size, sizeof(struct ami_schedule_message), + TAG_END); + nscb = pblHeapGetFirst(schedule_list); if(nscb == -1) return; @@ -216,12 +260,20 @@ void schedule_run(void) ami_schedule_remove_timer_event(nscb); pblHeapRemoveFirst(schedule_list); FreeVec(nscb); - callback(p); + + asmsg->type = AMI_S_RUN; + asmsg->callback = callback; + asmsg->p = p; + + PutMsg(nsmsgport, (struct Message *)asmsg); + + return; } -static void ami_schedule_open_timer(void) + +static struct MsgPort *ami_schedule_open_timer(void) { - msgport = AllocSysObjectTags(ASOT_PORT, + struct MsgPort *msgport = AllocSysObjectTags(ASOT_PORT, ASO_NoTrack,FALSE, TAG_DONE); @@ -235,61 +287,239 @@ static void ami_schedule_open_timer(void) TimerBase = (struct Device *)tioreq->Request.io_Device; ITimer = (struct TimerIFace *)GetInterface((struct Library *)TimerBase,"main",1,NULL); + + return msgport; } -static void ami_schedule_close_timer(void) +static void ami_schedule_close_timer(struct MsgPort *msgport) { if(ITimer) DropInterface((struct Interface *)ITimer); CloseDevice((struct IORequest *) tioreq); - FreeSysObject(ASOT_IOREQUEST,tioreq); - FreeSysObject(ASOT_PORT,msgport); + FreeSysObject(ASOT_IOREQUEST, tioreq); + FreeSysObject(ASOT_PORT, msgport); } -/* exported function documented in amiga/schedule.h */ -bool ami_schedule_create(void) +/** + * Initialise amiga scheduler + * + * /return true if initialised ok or false on error. + */ +static struct MsgPort *ami_schedule_create(void) { - ami_schedule_open_timer(); + struct MsgPort *msgport = ami_schedule_open_timer(); schedule_list = pblHeapNew(); - if(schedule_list == PBL_ERROR_OUT_OF_MEMORY) return false; + if(schedule_list == PBL_ERROR_OUT_OF_MEMORY) return NULL; pblHeapSetCompareFunction(schedule_list, ami_schedule_compare); - return true; + return msgport; } -/* exported function documented in amiga/schedule.h */ -void ami_schedule_free(void) +/** + * Finalise amiga scheduler + * + */ +static void ami_schedule_free(struct MsgPort *msgport) { schedule_remove_all(); pblHeapFree(schedule_list); // this should be empty at this point schedule_list = NULL; - ami_schedule_close_timer(); + ami_schedule_close_timer(msgport); } /* exported function documented in amiga/schedule.h */ nserror ami_schedule(int t, void (*callback)(void *p), void *p) +{ + if(smsgport == NULL) return NSERROR_INIT_FAILED; + + struct ami_schedule_message *asmsg = AllocSysObjectTags(ASOT_MESSAGE, + ASOMSG_Size, sizeof(struct ami_schedule_message), + TAG_END); + + asmsg->type = AMI_S_SCHEDULE; + asmsg->t = t; + asmsg->callback = callback; + asmsg->p = p; + + PutMsg(smsgport, (struct Message *)asmsg); + + return NSERROR_OK; +} + +static nserror ami_scheduler_schedule(struct ami_schedule_message *asmsg) { struct nscallback *nscb; if(schedule_list == NULL) return NSERROR_INIT_FAILED; - if (t < 0) return schedule_remove(callback, p); - - if ((nscb = ami_schedule_locate(callback, p, false))) { - return ami_schedule_reschedule(nscb, t); + if (asmsg->t < 0) return schedule_remove(asmsg->callback, asmsg->p); + + if ((nscb = ami_schedule_locate(asmsg->callback, asmsg->p, false))) { + return ami_schedule_reschedule(nscb, asmsg->t); } nscb = AllocVecTagList(sizeof(struct nscallback), NULL); if(!nscb) return NSERROR_NOMEM; - if (ami_schedule_add_timer_event(nscb, t) != NSERROR_OK) + if (ami_schedule_add_timer_event(nscb, asmsg->t) != NSERROR_OK) return NSERROR_NOMEM; - nscb->callback = callback; - nscb->p = p; + nscb->callback = asmsg->callback; + nscb->p = asmsg->p; pblHeapInsert(schedule_list, nscb); return NSERROR_OK; } +/* exported interface documented in amiga/schedule.h */ +void ami_schedule_handle(struct MsgPort *nsmsgport) +{ + /* nsmsgport is the NetSurf message port that the + * scheduler task is sending messages to. */ + struct ami_schedule_message *asmsg; + + while((asmsg = (struct ami_schedule_message *)GetMsg(nsmsgport))) { + if(asmsg->msg.mn_Node.ln_Type == NT_REPLYMSG) { + /* if it's a reply, free stuff */ + FreeSysObject(ASOT_MESSAGE, asmsg); + } else { + switch(asmsg->type) { + case AMI_S_STARTUP: + smsgport = asmsg->msg.mn_ReplyPort; + break; + + case AMI_S_RUN: + schedule_run(asmsg); + break; + + default: + // unknown message + break; + } + FreeSysObject(ASOT_MESSAGE, asmsg); /* don't reply, just free */ + } + } +} + + +static int32 ami_scheduler_process(STRPTR args, int32 length, APTR execbase) +{ + struct Process *proc = (struct Process *)FindTask(NULL); + struct MsgPort *nsmsgport = proc->pr_Task.tc_UserData; + struct MsgPort *schedulermsgport = AllocSysObjectTags(ASOT_PORT, TAG_END); + struct MsgPort *timermsgport = ami_schedule_create(); + bool running = true; + struct TimerRequest *timermsg = NULL; + ULONG schedulesig = 1L << schedulermsgport->mp_SigBit; + ULONG timersig = 1L << timermsgport->mp_SigBit; + uint32 signalmask = schedulesig | timersig; + uint32 signal = 0; + + /* Send a startup message to the message port we were given when we were created. + * This tells NetSurf where to send scheduler events to. */ + + struct ami_schedule_message *asmsg = AllocSysObjectTags(ASOT_MESSAGE, + ASOMSG_Size, sizeof(struct ami_schedule_message), + ASOMSG_ReplyPort, schedulermsgport, + TAG_END); + + asmsg->type = AMI_S_STARTUP; + PutMsg(nsmsgport, (struct Message *)asmsg); + + /* Main loop for this process */ + + while(running) { + signal = Wait(signalmask); + + if(signal & timersig) { + while((timermsg = (struct TimerRequest *)GetMsg(timermsgport))) { + ami_scheduler_run(nsmsgport); + //ReplyMsg((struct Message *)timermsg); /* \todo why does this crash? */ + } + } + + if(signal & schedulesig) { + while((asmsg = (struct ami_schedule_message *)GetMsg(schedulermsgport))) { + if(asmsg->msg.mn_Node.ln_Type == NT_REPLYMSG) { + /* if it's a reply, free stuff */ + FreeSysObject(ASOT_MESSAGE, asmsg); + } else { + switch(asmsg->type) { + case AMI_S_SCHEDULE: + ami_scheduler_schedule(asmsg); + break; + + case AMI_S_EXIT: + running = false; + break; + + default: + // unknown message + break; + } + FreeSysObject(ASOT_MESSAGE, asmsg); /* don't reply, just free */ + } + } + } + } + + ami_schedule_free(timermsgport); + + return RETURN_OK; +} + + +/** + * Create a new process for the scheduler. + * + * \param nsmsgport Message port to send scheduler events to. + * \return NSERROR_OK on success or error code on faliure. + */ +nserror ami_scheduler_process_create(struct MsgPort *nsmsgport) +{ + if(nsmsgport == NULL) return NSERROR_INIT_FAILED; + + struct Process *proc = CreateNewProcTags( + NP_Name, "NetSurf scheduler", + NP_Entry, ami_scheduler_process, + NP_Child, TRUE, + NP_StackSize, 16384, + NP_Priority, 1, + NP_UserData, nsmsgport, + TAG_DONE); + + if(proc == NULL) { + return NSERROR_NOMEM; + } + + LOG(("Waiting for scheduler process to start up...")); + + WaitPort(nsmsgport); + struct ami_schedule_message *asmsg = (struct ami_schedule_message *)GetMsg(nsmsgport); + + if(asmsg->type == AMI_S_STARTUP) { /* We shouldn't get any other messages at this stage */ + smsgport = asmsg->msg.mn_ReplyPort; + ReplyMsg((struct Message *)asmsg); + } + + LOG(("Scheduler started")); + + return NSERROR_OK; +} + +/* exported function documented in amiga/schedule.h */ +void ami_scheduler_process_delete(void) +{ + if(smsgport == NULL) return; + + struct ami_schedule_message *asmsg = AllocSysObjectTags(ASOT_MESSAGE, + ASOMSG_Size, sizeof(struct ami_schedule_message), + TAG_END); + + asmsg->type = AMI_S_EXIT; + PutMsg(smsgport, (struct Message *)asmsg); + + return; +} + diff --git a/amiga/schedule.h b/amiga/schedule.h index 9fcec9a82..cfea4d917 100755 --- a/amiga/schedule.h +++ b/amiga/schedule.h @@ -1,5 +1,5 @@ /* - * Copyright 2008 Chris Young + * Copyright 2008-2014 Chris Young * * This file is part of NetSurf, http://www.netsurf-browser.org/ * @@ -18,15 +18,8 @@ #ifndef AMIGA_SCHEDULE_H #define AMIGA_SCHEDULE_H -#include #include "amiga/os3support.h" -struct Device *TimerBase; -struct TimerIFace *ITimer; - -struct TimeRequest *tioreq; -struct MsgPort *msgport; - /** * Schedule a callback. * @@ -41,26 +34,23 @@ struct MsgPort *msgport; nserror ami_schedule(int t, void (*callback)(void *p), void *p); /** - * Initialise amiga scheduler + * Handle a message received from the scheduler process. * - * /return true if initialised ok or false on error. + * \param nsmsgport Message port to process. */ -bool ami_schedule_create(void); +void ami_schedule_handle(struct MsgPort *nsmsgport); /** - * Finalise amiga scheduler + * Create a new process for the scheduler. * + * \param nsmsgport Message port for the scheduler to send events to. + * \return NSERROR_OK on success or error code on failure. */ -void ami_schedule_free(void); +nserror ami_scheduler_process_create(struct MsgPort *nsmsgport); /** - * Process events up to current time. - * - * This implementation only takes the top entry off the heap, it does not - * venture to later scheduled events until the next time it is called - - * immediately afterwards, if we're in a timer signalled loop. + * Signal the scheduler process to exit. */ -void schedule_run(void); - +void ami_scheduler_process_delete(void); #endif