Build SDL as linklib.
[AROS-Contrib.git] / ScalosV2 / PPCThreadRootClass.c
blob13c5720bdc517b81b367a1646b5b78123dde907c
1 // tabsize ts=4
3 #include <proto/alib.h>
4 #include <proto/exec.h>
5 #include <proto/dos.h>
6 #include <proto/utility.h>
7 #include <proto/powerpc.h>
8 #include <intuition/classusr.h>
9 #include <exec/memory.h>
10 #include <dos/dosextens.h>
11 #include <dos/dostags.h>
12 #include <proto/powerpc.h>
13 #include <powerpc/powerpc.h>
14 #include <string.h>
16 #include "Scalos.h"
17 #include "ScalosIntern.h"
18 #include "PPCRootClass.h"
19 #include "SubRoutinesPPC.h"
20 #include "PPCThreadRootClass.h"
21 #include "MethodSenderClass.h"
23 #include "CompilerSupport.h"
24 #include "Debug.h"
26 #include "scalos_protos.h"
28 // replaces missing WarpOS functions
29 #define ObtainSemaphoreSharedPPC ObtainSemaphorePPC
30 #define AttemptSemaphoreSharedPPC AttemptSemaphorePPC
32 static char deftaskname[] = "private_object";
35 *--------------------------- ThreadTask -------------------------------
37 * This is the taskfunction of the object-thread. It will receive the
38 * startup message and then initiate the object. After this it will go
39 * into the input loop.
43 static void SAVEDS Thread_Task(void)
45 struct SCMSGP_Startup *startmsg;
46 Object *threadobj;
47 struct MsgPortPPC *msgport;
48 struct TaskPPC *owntask = FindTaskPPC(NULL);
50 LockTaskList();
51 msgport = owntask->tp_Msgport;
52 UnLockTaskList();
54 while (!(startmsg = (struct SCMSGP_Startup *) GetMsgPPC(msgport)))
55 WaitPortPPC(msgport);
57 threadobj = startmsg->threadobj;
58 if ((startmsg->scmsg.returnvalue = SC_DoMethodPPC(threadobj, SCCM_Init, startmsg->taglist)))
60 if (!(msgport = (struct MsgPortPPC *) getPPC(threadobj, SCCA_MsgPort)))
62 SC_DoMethodPPC(threadobj, SCCM_Exit);
63 startmsg->scmsg.returnvalue = NULL;
64 ReplyMsgPPC((struct Message *) startmsg);
65 return;
68 ReplyMsgPPC((struct Message *) startmsg);
69 while (!(SC_DoMethodPPC(threadobj, SCCM_Input)))
71 WaitPortPPC(msgport);
74 else
75 ReplyMsgPPC((struct Message *) startmsg);
77 return;
82 /*--------------------------- Functions --------------------------------*/
84 static ULONG ThreadRoot_New(struct SC_Class *cl, Object *obj, struct opSet *msg, struct ThreadRootInst *inst)
86 struct SCMSGP_Startup *startmsg;
87 struct TaskPPC *proc;
88 struct MsgPortPPC *replyport;
89 Object *senderobj;
90 char *taskname = (char *) &deftaskname;
92 DEBUGPPC("1\n");
94 if (((struct SC_Class *) obj)->ClassName)
95 taskname = ((struct SC_Class *) obj)->ClassName;
97 // the sender object that you get back
98 // methodsender is directly derived from root so that Object * for this
99 // object is the pointer to the beginning of its instance data
101 if (!(startmsg = (struct SCMSGP_Startup *) SC_AllocMsgPPC(SCMSG_STARTUP,sizeof(struct SCMSGP_Startup))))
102 return(NULL);
104 DEBUGPPC("2\n");
105 if ((replyport = CreateMsgPortPPC())) // only for startup msg
107 startmsg->taglist = msg->ops_AttrList;
108 SetReplyPortPPC((struct Message *) startmsg, replyport);
110 // make object
112 DEBUGPPC("3\n");
113 if ((inst = SC_AllocVecPPC(((struct SC_Class *) obj)->InstOffset + ((struct SC_Class *) obj)->InstSize + sizeof(struct ThreadRootInst), MEMF_CLEAR | MEMF_ANY)))
115 // set class pointer for the real object that we create
117 inst->oclass = (struct SC_Class *) obj;
119 // now we can execute methods of this object
121 // get "public" handle on baseclass instance from real beginning of obj data
123 obj = (Object *) (((struct ThreadRootInst *) inst) + 1);
125 // set obj pointer of real object in msg
127 startmsg->threadobj = obj;
129 // tell sender obj where to send its methods
131 DEBUGPPC("4\n");
132 if ((senderobj = SC_NewObjectPPC(NULL, SCC_METHODSENDER_NAME,SCCA_MethodSender_DestObject, obj, TAG_DONE)))
134 DEBUGPPC("5\n");
135 if ((proc = CreateTaskPPCTags(TASKATTR_CODE,Thread_Task, TASKATTR_NAME,taskname, TASKATTR_STACKSIZE,16384, TAG_DONE)))
137 struct MsgPortPPC *msgsendmsgport;
138 LockTaskList();
139 msgsendmsgport = proc->tp_Msgport;
140 UnLockTaskList();
142 DEBUGPPC("6\n");
143 PutMsgPPC(msgsendmsgport, (struct Message *) startmsg); // send StartupMessage
144 while (!GetMsgPPC(replyport))
145 WaitPortPPC(replyport); // wait for the reply
147 DEBUGPPC("7\n");
148 // the first received message must be our startupmessage
149 if (startmsg->scmsg.returnvalue)
151 DEBUGPPC("8\n");
152 DeleteMsgPortPPC(replyport);
153 SC_FreeMsgPPC((struct SC_Message *) startmsg);
154 SCOCLASS(obj)->ObjectCount += 1;
155 SetCache(CACHE_DCACHEFLUSH, inst, sizeof(struct ThreadRootInst));
156 return((ULONG) senderobj);
159 SC_FreeVecPPC(inst);
160 SC_SetAttrsPPC(senderobj,SCCA_MethodSender_DestObject,NULL,TAG_DONE);
161 SC_DisposeObjectPPC(senderobj);
164 DeleteMsgPortPPC(replyport);
166 SC_FreeMsgPPC((struct SC_Message *) startmsg);
167 return(NULL);
170 static ULONG ThreadRoot_Dispose(struct SC_Class *cl, Object *obj, Msg msg, struct ThreadRootInst *inst)
172 SC_DoMethodPPC(obj, SCCM_Exit);
173 SCOCLASS(obj)->ObjectCount -= 1;
174 SC_FreeVecPPC(myThreadRootInst(obj));
175 return(0);
178 /****** ThreadRoot.scalos/SCCM_Init *****************************************
180 * NAME
181 * SCCM_Init
183 * SYNOPSIS
184 * BOOL SC_DoMethod(obj,SCCM_Init,struct TagItem *taglist);
186 * FUNCTION
187 * This is the standard init function. It replaces the old OM_NEW method.
188 * The Rootclass will initialize the lists (notification list and object
189 * list) and create a messageport.
191 * INPUTS
192 * taglist - the taglist for the init method attributes
194 * RESULT
195 * TRUE if the init method was successfull.
197 * NOTES
198 * The method will be called in the object that runs on a new thread.
199 * NEVER USE OM_NEW! You'll get the wrong object and instance data!
201 * SEE ALSO
202 * SCCM_Exit
204 *****************************************************************************
207 static ULONG ThreadRoot_Init(struct SC_Class *cl, Object *obj, struct SCCP_Init * msg, struct ThreadRootInst *inst)
209 inst = myThreadRootInst(obj);
211 NewList((struct List *) &inst->objectlist);
212 NewList((struct List *) &inst->notilist);
213 if (InitSemaphorePPC(&inst->objectlistsem))
215 if (InitSemaphorePPC(&inst->notilistsem))
217 if ((inst->msgport = CreateMsgPortPPC()))
218 return(TRUE);
219 else
220 RemSemaphorePPC(&inst->notilistsem);
222 RemSemaphorePPC(&inst->objectlistsem);
224 return(FALSE);
227 /****** ThreadRoot.scalos/SCCM_Exit *****************************************
229 * NAME
230 * SCCM_Exit
232 * SYNOPSIS
233 * SC_DoMethod(obj,SCCM_Exit);
235 * FUNCTION
236 * This is the replacement for OM_DISPOSE. Here your object should free all
237 * resources.
239 * INPUTS
241 * RESULT
243 * NOTES
244 * SCCM_Exit will be called from the thread of the object.
246 * SEE ALSO
247 * SCCM_Init
249 *****************************************************************************
252 static ULONG ThreadRoot_Exit(struct SC_Class *cl, Object *obj, Msg msg, struct ThreadRootInst *inst)
254 struct MinNode *node;
256 inst = myThreadRootInst(obj);
257 ObtainSemaphorePPC(&inst->objectlistsem);
258 while (!(IsListEmpty((struct List *) &inst->objectlist)))
260 node = inst->objectlist.mlh_Head;
261 RemovePPC((struct Node *) node);
262 SC_DoMethodPPC(SCBASEOBJECT(node), OM_DISPOSE);
264 ReleaseSemaphorePPC(&inst->objectlistsem);
265 ObtainSemaphorePPC(&inst->notilistsem);
266 FreeAllNodesPPC(&inst->notilist);
267 ReleaseSemaphorePPC(&inst->notilistsem);
268 DeleteMsgPortPPC(inst->msgport);
269 return(0);
272 /****** ThreadRoot.scalos/OM_ADDMEMBER **************************************
274 * NAME
275 * OM_ADDMEMBER
277 * SYNOPSIS
278 * SC_DoMethod(obj,OM_ADDMEMBER,Object *childobj);
280 * FUNCTION
281 * Adds an object to the child list of this object.
283 * INPUTS
284 * childobj - Object to add.
286 * RESULT
288 * NOTES
290 * SEE ALSO
291 * OM_REMMEMBER
293 *****************************************************************************
296 static ULONG ThreadRoot_AddMember(struct SC_Class *cl, Object *obj, struct opMember *msg, struct ThreadRootInst *inst)
298 inst = myThreadRootInst(obj);
300 ObtainSemaphorePPC(&inst->objectlistsem);
301 AddTailPPC((struct List *) &inst->objectlist, (struct Node *) &_SCOBJECT(msg->opam_Object)->sco_Node);
302 ReleaseSemaphorePPC(&inst->objectlistsem);
303 return(0);
306 /****** ThreadRoot.scalos/OM_REMMEMBER **************************************
308 * NAME
309 * OM_REMMEMBER
311 * SYNOPSIS
312 * SC_DoMethod(obj,OM_REMMEMBER,Object *childobj);
314 * FUNCTION
315 * Removes an object from the child list of this object.
317 * INPUTS
318 * childobj - Object to remove.
320 * RESULT
322 * NOTES
324 * SEE ALSO
325 * OM_ADDMEMBER
327 *****************************************************************************
330 static ULONG ThreadRoot_RemMember(struct SC_Class *cl, Object *obj, struct opMember *msg, struct ThreadRootInst *inst)
332 inst = myThreadRootInst(obj);
334 ObtainSemaphorePPC(&inst->objectlistsem);
335 RemovePPC((struct Node *) &_SCOBJECT(obj)->sco_Node);
336 ReleaseSemaphorePPC(&inst->objectlistsem);
337 return(0);
340 /****** ThreadRoot.scalos/SCCM_Notify ***************************************
342 * NAME
343 * SCCM_Notify
345 * SYNOPSIS
346 * SC_DoMethod(obj,SCCM_Notify,ULONG TriggerAttr, ULONG TriggerVal, Object *DestObj, ULONG NumArgs, ...);
348 * FUNCTION
349 * This method adds a notify to the notification list.
350 * If an attribute was set, the Rootclass will check the notification list
351 * for an attribute to trigger. If so and the attribute has changed and
352 * is the same as the trigger value the Rootclass will call a method in
353 * the given DestObj. Attributes in the DestObj can be set with the SCCM_Set
354 * method.
355 * SCCV_TriggerValue reflects the state of the attribute that triggers the
356 * destination attribute, if any.
357 * If you want to change an attribute as the result of a notification you
358 * can use SCCV_TriggerValue, which reflects the state of the source
359 * attribute TriggerAttr as the value for the destination attribute to
360 * change the attribute of the destination object to the same value.
361 * Of course the possible values of the source attribute should make sense
362 * to the destination attribute.
364 * INPUTS
365 * TriggerAttr - an attribute to trigger
366 * TriggerVal - the value when the notify should be done or
367 * SCCV_EveryTime if the notify should be done when every the
368 * triggerattr was changed
369 * DestObj - destination object to do the notify method
370 * NumArgs - number of following arguments
372 * RESULT
374 * NOTES
376 * SEE ALSO
378 *****************************************************************************
381 static ULONG ThreadRoot_Notify(struct SC_Class *cl, Object *obj, struct SCCP_Notify *msg, struct ThreadRootInst *inst)
383 struct NotifyNode *buffer;
384 ULONG cpsize = (msg->numargs)*sizeof(ULONG) + sizeof(struct NotifyNode) - 3*sizeof(ULONG); // complete parameters length
386 inst = myThreadRootInst(obj);
387 ObtainSemaphorePPC(&inst->notilistsem);
388 if ((buffer = (struct NotifyNode *) AllocNodePPC(&inst->notilist,cpsize)))
389 memcpy(&buffer->TriggerAttr, &msg->TriggerAttr, cpsize - sizeof(struct MinNode));
390 ReleaseSemaphorePPC(&inst->notilistsem);
391 return(TRUE);
394 void ThreadRoot_Set(struct SC_Class *cl, Object *obj, struct opSet *msg, struct ThreadRootInst *inst)
396 struct TagItem *tmptaglist = msg->ops_AttrList;
397 struct TagItem **taglist = &tmptaglist;
398 struct TagItem *tag;
399 struct NotifyNode *node;
400 ULONG tmpvalue;
401 ULONG tmparray[10];
402 ULONG *srcarray;
403 ULONG *destarray;
404 int i;
406 inst = myThreadRootInst(obj);
407 ObtainSemaphoreSharedPPC(&inst->notilistsem);
408 while ((tag = NextTagItemPPC(taglist))) // search in taglist
410 // look for a attribute that we should trigger
411 for (node = (struct NotifyNode *) inst->notilist.mlh_Head; node->node.mln_Succ; node = (struct NotifyNode *) node->node.mln_Succ)
413 if (tag->ti_Tag == node->TriggerAttr) // if found
415 if ( !(SC_GetAttrPPC(tag->ti_Tag,obj,&tmpvalue)) || (tag->ti_Data != tmpvalue) ) // if current attribute from object doesn`t exist or the attribute has been changed
417 /* execute only if attr-value has changed */
418 if ((node->TriggerVal == SCCV_EveryTime) || (tag->ti_Data == node->TriggerVal)) // SCCV_EveryTime: notify everytime the values changes
420 if (node->arg_method == SCCM_Set)
422 if (node->arg_value == SCCV_TriggerValue)
423 SC_SetAttrsPPC(node->DestObject, node->arg_attr, tag->ti_Data, TAG_DONE);
424 else
425 SC_SetAttrsPPC(node->DestObject, node->arg_attr, node->arg_value);
427 else
429 srcarray = &node->arg_method;
430 if (node->numargs > 10)
431 destarray = SC_AllocVecPPC(node->numargs * sizeof(ULONG), MEMF_PUBLIC);
432 else
433 destarray = (ULONG *) &tmparray;
434 for (i = 0; i < node->numargs; i++)
436 if (srcarray[i] == SCCV_TriggerValue)
437 destarray[i] = tag->ti_Data;
438 else
439 destarray[i] = srcarray[i];
441 SC_DoMethodAPPC(node->DestObject, (Msg) destarray);
442 if (node->numargs > 10)
443 SC_FreeVecPPC(destarray);
449 ReleaseSemaphorePPC(&inst->notilistsem);
452 /****** ThreadRoot.scalos/SCCM_Input ****************************************
454 * NAME
455 * SCCM_Input
457 * SYNOPSIS
458 * SC_DoMethod(obj,SCCM_Input);
460 * FUNCTION
461 * This method manages the input handling of the thread. It's a simple
462 * message loop which understands some internal messages too.
464 * INPUTS
466 * RESULT
468 * NOTES
469 * If your method needs some time to be executed you should call this
470 * method from time to time to avoid locking situations so that a
471 * potential sender won`t lock because you don`t receive its message
472 * (generated from its method).
474 * SEE ALSO
476 *****************************************************************************
478 * InternReply - checks the message before reply. If it's already a reply or
479 * it has no replyport, then it will free the message.
482 static void InternReply(struct Message *msg)
484 if ((msg->mn_Node.ln_Type == NT_REPLYMSG) || (!(msg->mn_ReplyPort)))
485 SC_FreeMsgPPC((struct SC_Message *) msg);
486 else
487 ReplyMsgPPC(msg);
490 static ULONG ThreadRoot_Input(struct SC_Class *cl, Object *obj, Msg msg, struct ThreadRootInst *inst)
492 struct Message *message;
493 ULONG msgtype;
495 inst = myThreadRootInst(obj);
496 while ((message = GetMsgPPC((struct MsgPortPPC *) inst->msgport)))
498 if (message->mn_Node.ln_Type == NT_MESSAGE)
500 if ((msgtype = SC_IsScalosMsgPPC(message)))
502 if (msgtype == SCMSG_METHOD) // internal msg for a method
504 ((struct SC_Message *) message)->returnvalue = SC_DoMethodAPPC(obj, (Msg) &((struct SCMSGP_Method *) message)->methodarg1);
505 if (((struct SCMSGP_Method *) message)->methodarg1 == OM_DISPOSE)
507 InternReply(message);
508 return(TRUE);
510 InternReply(message);
512 else
514 SC_DoMethodPPC(obj, SCCM_MessageReceived, message);
515 InternReply(message);
518 else
520 SC_DoMethodPPC(obj, SCCM_MessageReceived, message);
521 ReplyMsgPPC(message);
524 else
526 SC_DoMethodPPC(obj, SCCM_ReplyReceived, message);
529 return(FALSE);
533 /****** ThreadRoot.scalos/SCCM_MessageReceived ******************************
535 * NAME
536 * SCCM_MessageReceived
538 * SYNOPSIS
539 * SC_DoMethod(obj,SCCM_MessageReceived, struct Message *message);
541 * FUNCTION
542 * Your object will get this method if the thread has received a message
543 * that scalos doesn`t handle in SCCM_Input.
545 * INPUTS
546 * message - exec message which was received by your thread
548 * RESULT
550 * NOTES
552 * SEE ALSO
554 *****************************************************************************
557 /****** ThreadRoot.scalos/SCCM_ReplyReceived ********************************
559 * NAME
560 * SCCM_ReplyReceived
562 * SYNOPSIS
563 * SC_DoMethod(obj,SCCM_ReplyReceived, struct Message *message);
565 * FUNCTION
566 * Your object will get this method if the thread has received an unknown
567 * (non scalos) message and it is a reply message. If this is your message,
568 * free the data and don't call your superclass for this method.
570 * INPUTS
571 * message - exec message which was received by your thread
573 * RESULT
575 * NOTES
577 * SEE ALSO
579 *****************************************************************************
581 static void ThreadRoot_ReplyReceived(struct SC_Class *cl, Object *obj, struct SCCP_ReplyReceived *msg, struct ThreadRootInst *inst)
583 if (SC_IsScalosMsgPPC(msg->message))
584 SC_FreeMsgPPC((struct SC_Message *) msg->message);
587 /****** ThreadRoot.scalos/SCCM_LockObjectList *******************************
589 * NAME
590 * SCCM_LockObjectList
592 * SYNOPSIS
593 * struct MinList *SC_DoMethod(obj,SCCM_LockObjectList, ULONG locktype);
595 * FUNCTION
596 * Using this method you can access the objectlist of an object. This list
597 * is fully semaphore protect.
598 * The return value can be NULL even for exclusive or shared locks, because
599 * the root class may provide a semaphore timeout in future.
601 * INPUTS
602 * locktype - one for these values :
603 * SCCV_LockExclusive - for write access to the list, only one
604 * task can have a lock in the at one time
605 * SCCV_LockShared - for read access to the list, multiple
606 * tasks can have read access on list
607 * SCCV_LockAttempt - try to lock the list for a exclusiv lock,
608 * no waiting will be done
609 * SCCV_LockAttemptShared - try to lock the list for a shared
610 * lock
612 * RESULT
613 * The objectlist or NULL if the lock fails.
615 * NOTES
616 * If this function was successful you have to release the lock using the
617 * SCCM_UnlockObjectList method.
619 * SEE ALSO
620 * SCCM_UnlockObjectList, OM_ADDMEMBER, OM_REMMEMBER
622 *****************************************************************************
625 static struct MinList *ThreadRoot_LockObjectList(struct SC_Class *cl, Object *obj, struct SCCP_LockObjectList *msg, struct ThreadRootInst *inst)
627 inst = myThreadRootInst(obj);
628 switch (msg->locktype)
630 case SCCV_LockExclusive :
631 ObtainSemaphorePPC(&inst->objectlistsem);
632 return(&inst->objectlist);
634 case SCCV_LockShared :
635 ObtainSemaphoreSharedPPC(&inst->objectlistsem);
636 return(&inst->objectlist);
638 case SCCV_LockAttempt :
639 if (AttemptSemaphorePPC(&inst->objectlistsem))
640 return(&inst->objectlist);
641 else
642 return(NULL);
644 case SCCV_LockAttemptShared :
645 if (AttemptSemaphoreSharedPPC(&inst->objectlistsem))
646 return(&inst->objectlist);
647 else
648 return(NULL);
652 /****** ThreadRoot.scalos/SCCM_UnlockObjectList *****************************
654 * NAME
655 * SCCM_UnlockObjectList
657 * SYNOPSIS
658 * SC_DoMethod(obj,SCCM_UnlockObjectList);
660 * FUNCTION
661 * Release the lock on the objectlist which was made using
662 * SCCM_LockObjectList.
664 * INPUTS
666 * RESULT
668 * NOTES
670 * SEE ALSO
671 * SCCM_LockObjectList, OM_ADDMEMBER, OM_REMMEMBER
673 *****************************************************************************
676 static void ThreadRoot_UnlockObjectList(struct SC_Class *cl, Object *obj, Msg msg, struct ThreadRootInst *inst)
678 inst = myThreadRootInst(obj);
679 ReleaseSemaphorePPC(&inst->objectlistsem);
682 /* ----------------------------------------------------------------------- */
684 static ULONG ThreadRoot_Get( struct SC_Class *cl, Object *obj, struct opGet *msg, struct ThreadRootInst *inst )
686 inst = myThreadRootInst(obj);
688 if (msg->opg_AttrID == SCCA_MsgPort)
690 *(msg->opg_Storage) = (ULONG) inst->msgport;
691 return( TRUE );
693 return(FALSE);
696 /* ----------------------------------------------------------------------- */
698 static ULONG ThreadRoot_Default(struct SC_Class *cl, Object *obj, Msg msg, struct ThreadRootInst *inst)
700 return(0);
703 struct SC_MethodData PPCThreadRootMethods[] =
705 { OM_NEW, (ULONG) ThreadRoot_New, sizeof(struct opSet), SCMDF_FULLFLUSH, NULL },
706 { OM_DISPOSE, (ULONG) ThreadRoot_Dispose, sizeof(ULONG), 0, NULL },
707 { OM_SET, (ULONG) ThreadRoot_Set, sizeof(struct opSet), SCMDF_FULLFLUSH, NULL },
708 { OM_GET, (ULONG) ThreadRoot_Get, sizeof(struct opGet), SCMDF_FULLFLUSH, NULL },
709 { OM_ADDMEMBER, (ULONG) ThreadRoot_AddMember, sizeof(struct opMember), 0, NULL },
710 { OM_REMMEMBER, (ULONG) ThreadRoot_RemMember, sizeof(struct opMember), 0, NULL },
711 { SCCM_Init, (ULONG) ThreadRoot_Init, sizeof(struct SCCP_Init), SCMDF_FULLFLUSH, NULL },
712 { SCCM_Exit, (ULONG) ThreadRoot_Exit, sizeof(ULONG), 0, NULL },
713 { SCCM_Notify, (ULONG) ThreadRoot_Notify, sizeof(struct SCCP_Notify) + sizeof(ULONG)*32, 0, NULL }, // maximal 32 args with notifies
714 { SCCM_Input, (ULONG) ThreadRoot_Input, sizeof(ULONG), 0, NULL },
715 { SCCM_MessageReceived, NULL, sizeof(struct SCCP_MessageReceived), 0, NULL },
716 { SCCM_ReplyReceived, (ULONG) ThreadRoot_ReplyReceived, sizeof(struct SCCP_ReplyReceived), 0, NULL },
717 { SCCM_LockObjectList, (ULONG) ThreadRoot_LockObjectList, sizeof(struct SCCP_LockObjectList), 0, NULL },
718 { SCCM_UnlockObjectList, (ULONG) ThreadRoot_UnlockObjectList, sizeof(ULONG), 0, NULL },
719 { SCMETHOD_DONE, (ULONG) ThreadRoot_Default, 0, 0, NULL }