root/branches/as-branch/src/common/misc/state-machine-fns.c @ 7826

Revision 7826, 26.1 KB (checked in by sson, 4 years ago)

Used PINT_sm_frame(smcb->parent_smcb, 0) to refer parent smcbs in pipeline.sm.
Removed possible double free() in io.sm.

Line 
1/*
2 * (C) 2001 Clemson University and The University of Chicago
3 *
4 * See COPYING in top-level directory.
5 */
6
7#ifndef __STATE_MACHINE_FNS_H
8#define __STATE_MACHINE_FNS_H
9
10#include <stdio.h>
11#include <string.h>
12#include <assert.h>
13#include <stdlib.h> /* sson */
14
15#include "gossip.h"
16#include "pvfs2-debug.h"
17#include "state-machine.h"
18#include "client-state-machine.h"
19
20struct PINT_frame_s
21{
22    int task_id;
23    void *frame;
24    int error;
25    struct qlist_head link;
26};
27
28static struct PINT_state_s *PINT_pop_state(struct PINT_smcb *);
29static void PINT_push_state(struct PINT_smcb *, struct PINT_state_s *);
30static struct PINT_state_s *PINT_sm_task_map(struct PINT_smcb *smcb, int task_id);
31static void PINT_sm_start_child_frames(struct PINT_smcb *smcb, int* children_started);
32
33/* Function: PINT_state_machine_halt(void)
34   Params: None
35   Returns: True
36   Synopsis: This function is used to shutdown the state machine
37 */
38int PINT_state_machine_halt(void)
39{
40    return 0;
41}
42
43/* Function: PINT_state_machine_terminate
44   Params: smcb, job status
45   Returns: 0 on sucess, otherwise error
46   Synopsis: This function cleans up and terminates a SM
47        in some cases we may need to keep the SM alive until
48        its children terminate or something - in which case we
49        will post the relevant job here.
50 */
51int PINT_state_machine_terminate(struct PINT_smcb *smcb, job_status_s *r)
52{
53    struct PINT_frame_s *f;
54    void *my_frame;
55    job_id_t id;
56
57    /* notify parent */
58    if (smcb->parent_smcb)
59    {
60        gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
61                     "[SM Terminating Child]: (%p) (error_code: %d)\n",
62                     smcb,
63                     /* skip pvfs2_ */
64                     (int32_t)r->error_code);
65         assert(smcb->parent_smcb->children_running > 0);
66
67         my_frame = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
68         /* this will loop from TOS down to the base frame */
69         /* base frame will not be processed */
70         qlist_for_each_entry(f, &smcb->parent_smcb->frames, link)
71         {
72             if(my_frame == f->frame)
73             {
74                 f->error = r->error_code;
75                 break;
76             }
77         }
78
79        if (--smcb->parent_smcb->children_running <= 0)
80        {
81            /* no more child state machines running, so we can
82             * start up the parent state machine again
83             */
84            job_null(0, smcb->parent_smcb, 0, r, &id, smcb->context);
85        }
86        return SM_ACTION_DEFERRED;
87    }
88    /* call state machine completion function */
89    if (smcb->terminate_fn)
90    {
91        (*smcb->terminate_fn)(smcb, r);
92    }
93    return 0;
94}
95
96/* Function: PINT_state_machine_invoke
97   Params: smcb pointer and job status pointer
98   Returns: return value of state action
99   Synopsis: runs the current state action, produces debugging
100        output if needed, checls return value and takes action
101        if needed (sets op_terminate if SM_ACTION_TERMINATED is
102        returned)
103 */
104PINT_sm_action PINT_state_machine_invoke(struct PINT_smcb *smcb,
105                                         job_status_s *r)
106{
107    PINT_sm_action retval;
108    const char * state_name;
109    const char * machine_name;
110    int children_started = 0;
111
112    if (!(smcb) || !(smcb->current_state) ||
113            !(smcb->current_state->flag == SM_RUN ||
114              smcb->current_state->flag == SM_PJMP) ||
115            !(smcb->current_state->action.func))
116    {
117        gossip_err("SM invoke called on invalid smcb or state\n");
118        return SM_ERROR;
119    }
120
121    state_name = PINT_state_machine_current_state_name(smcb);
122    machine_name = PINT_state_machine_current_machine_name(smcb);
123
124    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
125                 "[SM Entering]: (%p) %s:%s (status: %d)\n",
126                 smcb,
127                 /* skip pvfs2_ */
128                 machine_name,
129                 state_name,
130                 (int32_t)r->status_user_tag);
131
132    /* call state action function */
133    retval = (smcb->current_state->action.func)(smcb,r);
134    /* process return code */
135    switch (retval)
136    {
137    case SM_ACTION_TERMINATE :
138            smcb->op_terminate = 1;
139            break;
140    case SM_ACTION_COMPLETE :
141    case SM_ACTION_DEFERRED :
142            break;
143    default :
144            /* error */
145            gossip_err("SM Action %s:%s returned invalid return code %d (%p)\n",
146                       machine_name, state_name, retval, smcb);
147            break;
148    }
149
150    /* print post-call debugging info */
151    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
152                 "[SM Exiting]: (%p) %s:%s (error code: %d), (action: %s)\n",
153                 smcb,
154                 /* skip pvfs2_ */
155                 machine_name,
156                 state_name,
157                 r->error_code,
158                 SM_ACTION_STRING(retval));
159
160    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
161                 "retval=%d, current_state->flag=%d, children_running=%d\n",
162                 retval,
163                 smcb->current_state->flag,
164                 smcb->children_running); /* sson */
165    if (retval == SM_ACTION_COMPLETE && smcb->current_state->flag == SM_PJMP)
166    {
167        /* start child SMs */
168        PINT_sm_start_child_frames(smcb, &children_started);
169        /* if any children were started, then we return DEFERRED (even
170         * though they may have all completed immediately).  The last child
171         * issues a job_null that will drive progress from here and we don't
172         * want to cause a double transition.
173         */
174        gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "smcb=%p: children_started=%d\n", smcb, children_started);
175        if (children_started > 0)
176            retval = SM_ACTION_DEFERRED;
177        else
178            retval = SM_ACTION_COMPLETE;
179    }
180
181    return retval;
182}
183
184/* Function: PINT_state_machine_start()
185   Params: smcb pointer and job status pointer
186   Returns: return value of last state action
187   Synopsis: Runs the state action pointed to by the
188        current state, then continues to run the SM
189        as long as return code is SM_ACTION_COMPLETE.
190 */
191
192PINT_sm_action PINT_state_machine_start(struct PINT_smcb *smcb, job_status_s *r)
193{
194    PINT_sm_action ret;
195
196    /* set the state machine to being completed immediately.  We
197     * unset this bit once the state machine is deferred.
198     */
199    smcb->immediate = 1;
200
201    /* set the base frame to be the current TOS, which should be 0 */
202    smcb->base_frame = smcb->frame_count - 1;
203
204    /* run the current state action function */
205    ret = PINT_state_machine_invoke(smcb, r);
206    if (ret == SM_ACTION_COMPLETE || ret == SM_ACTION_TERMINATE)
207    {
208        /* keep running until state machine deferrs or terminates */
209        ret = PINT_state_machine_continue(smcb, r);
210    }
211
212    if(ret == SM_ACTION_DEFERRED)
213    {
214        /* this state machine isn't completing immediately */
215        smcb->immediate = 0;
216    }
217
218    return ret;
219}
220
221/* Function: PINT_state_machine_next()
222   Params: smcb pointer and job status pointer
223   Returns: return value of last state action
224   Synopsis: Runs through a list of return values to find the next function to
225   call.  Calls that function.  If that function returned COMPLETED loop
226   and repeat.
227 */
228PINT_sm_action PINT_state_machine_next(struct PINT_smcb *smcb, job_status_s *r)
229{
230    int i; /* index for transition table */
231    struct PINT_tran_tbl_s *transtbl;
232    PINT_sm_action ret;   /* holds state action return code */
233
234    if (!smcb)
235    {
236        gossip_err("SM next called on invald smcb\n");
237        return -1;
238    }
239    if(PINT_smcb_cancelled(smcb))
240    {
241        return SM_ACTION_TERMINATE;
242    }
243
244    /* loop while invoke of new state returns COMPLETED */
245    do {
246        /* loop while returning from nested SM */
247        do {
248            if (!smcb->current_state || !smcb->current_state->trtbl)
249            {
250                gossip_err("SM current state or trtbl is invalid "
251                           "(smcb = %p)\n", smcb);
252                gossip_backtrace();
253                assert(0);
254                return -1;
255            }
256            transtbl = smcb->current_state->trtbl;
257
258            /* for each entry in the transition table there is a return
259             * code followed by a next state pointer to the new state.
260             * This loops through each entry, checking for a match on the
261             * return address, and then sets the new current_state and calls
262             * the new state action function */
263            for (i = 0; transtbl[i].return_value != DEFAULT_ERROR; i++)
264            {
265                if (transtbl[i].return_value == r->error_code)
266                    break;
267            }
268            /* we expect the last state action function to return
269            * SM_ACTION_TERMINATE which sets the smcb->op_terminate
270            * flag.  ALSO the state machine must direct the next state
271            * to be terminate, which sets loc->flag to SM_TERMINATE.
272            * We'll terminate for EITHER, but print an error if not
273            * both.
274            */
275            if(transtbl[i].flag == SM_TERM || smcb->op_terminate)
276            {
277                if (!(transtbl[i].flag == SM_TERM))
278                {
279                    gossip_lerr("Error: state machine returned"
280                           " SM_ACTION_TERMINATE but didn't reach terminate\n");
281                }
282                if (!smcb->op_terminate)
283                {
284                    gossip_lerr("Error: state machine reached terminate"
285                            " without returning SM_ACTION_TERMINATE\n");
286                    smcb->op_terminate = 1;
287                }
288                return SM_ACTION_TERMINATE;
289            }
290            if (transtbl[i].flag == SM_RETURN)
291            {
292                /* if this is a return pop the stack
293                 * and we'll continue from the state returned to
294                 */
295                smcb->current_state = PINT_pop_state(smcb);
296                if(!smcb->current_state ||
297                   smcb->current_state->trtbl[0].flag == SM_TERM)
298                {
299                    /* assume nested state machine was invoked without
300                     * a parent */
301                    return SM_ACTION_TERMINATE;
302                }
303            }
304        } while (transtbl[i].flag == SM_RETURN);
305        smcb->current_state = transtbl[i].next_state;
306        /* To do nested states, we check to see if the next state is
307        * a nested state machine, and if so we push the return state
308        * onto a stack */
309        while (smcb->current_state->flag == SM_JUMP)
310        {
311            gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "%s: SM_JUMP\n", __func__);
312            PINT_push_state(smcb, smcb->current_state);
313            smcb->current_state =
314                    smcb->current_state->action.nested->first_state;
315        }
316        /* runs state_action and returns the return code */
317        ret = PINT_state_machine_invoke(smcb, r);
318    } while (ret == SM_ACTION_COMPLETE || ret == SM_ACTION_TERMINATE);
319    return ret;
320}
321
322/* Function: PINT_state_machine_continue
323   Params: smcb pointer and job status pointer
324   Returns: return value of last state action
325   Synopsis: This function essentially calls next, and if the state
326             machine terminates, calls terminate to perform cleanup.
327             This allows separation from the start call (which calls
328             next but does not call terminate if the state machine
329             terminates).
330*/
331PINT_sm_action PINT_state_machine_continue(struct PINT_smcb *smcb, job_status_s *r)
332{
333    PINT_sm_action ret;
334
335    ret = PINT_state_machine_next(smcb, r);
336
337    if(ret == SM_ACTION_TERMINATE)
338    {
339        /* process terminating SM */
340        PINT_state_machine_terminate(smcb, r);
341    }
342
343    return ret;
344}
345
346/* Function: PINT_state_machine_locate(void)
347   Params:   smcb pointer with op correctly set
348   Returns:  1 on successful locate, 0 on locate failure, <0 on error
349   Synopsis: This function locates the state associated with the op
350             specified in smcb->op in order to start a state machine's
351             execution.
352 */
353int PINT_state_machine_locate(struct PINT_smcb *smcb)
354{
355    struct PINT_state_s *current_tmp;
356    struct PINT_state_machine_s *op_sm;
357    const char *state_name;
358    const char *machine_name;
359
360    /* check for valid inputs */
361    if (!smcb || smcb->op < 0 || !smcb->op_get_state_machine)
362    {
363        gossip_err("State machine requested not valid\n");
364        return -PVFS_EINVAL;
365    }
366    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
367            "[SM Locating]: (%p) op-id: %d\n",smcb,(smcb)->op);
368    /* this is a the usage dependant routine to look up the SM */
369    op_sm = (*smcb->op_get_state_machine)(smcb->op);
370    if (op_sm != NULL)
371    {
372        current_tmp = op_sm->first_state;
373        /* handle the case in which the first state points to a nested
374         * machine, rather than a simple function
375         */
376        while(current_tmp->flag == SM_JUMP)
377        {
378            PINT_push_state(smcb, current_tmp);
379            current_tmp = ((struct PINT_state_machine_s *)
380                           current_tmp->action.nested)->first_state;
381        }
382        smcb->current_state = current_tmp;
383
384        state_name = PINT_state_machine_current_state_name(smcb);
385        machine_name = PINT_state_machine_current_machine_name(smcb);
386
387        gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
388                     "[SM Locating]: (%p) located: %s:%s\n",
389                     smcb, machine_name, state_name);
390
391        return 1; /* indicates successful locate */
392    }
393    gossip_err("State machine not found for operation %d\n",smcb->op);
394    return 0; /* indicates failed to locate */
395}
396
397/* Function: PINT_smcb_set_op
398   Params: pointer to an smcb pointer, and an op code (int)
399   Returns: nothing
400   Synopsis: sets op on existing smcb and reruns locate if
401            we have a valid locate func
402 */
403int PINT_smcb_set_op(struct PINT_smcb *smcb, int op)
404{
405    smcb->op = op;
406    return PINT_state_machine_locate(smcb);
407}
408
409int PINT_smcb_immediate_completion(struct PINT_smcb *smcb)
410{
411    return smcb->immediate;
412}
413
414/* Function: PINT_smcb_op
415   Params: pointer to an smcb pointer
416   Returns: op (int)
417   Synopsis: returns the op currently set in the smcb
418 */
419int PINT_smcb_op(struct PINT_smcb *smcb)
420{
421    return smcb->op;
422}
423
424static int PINT_smcb_sys_op(struct PINT_smcb *smcb)
425{
426    if (smcb->op > 0 && smcb->op < PVFS_OP_SYS_MAXVALID)
427        return 1;
428    return 0;
429}
430
431static int PINT_smcb_mgmt_op(struct PINT_smcb *smcb)
432{
433    if (smcb->op > PVFS_OP_SYS_MAXVAL && smcb->op < PVFS_OP_MGMT_MAXVALID)
434        return 1;
435    return 0;
436}
437
438static int PINT_smcb_misc_op(struct PINT_smcb *smcb)
439{
440    return smcb->op == PVFS_SERVER_GET_CONFIG
441        || smcb->op == PVFS_SERVER_FETCH_CONFIG
442        || smcb->op == PVFS_CLIENT_JOB_TIMER
443        || smcb->op == PVFS_CLIENT_PERF_COUNT_TIMER
444        || smcb->op == PVFS_DEV_UNEXPECTED;
445}
446
447int PINT_smcb_invalid_op(struct PINT_smcb *smcb)
448{
449    if (!PINT_smcb_sys_op(smcb) && !PINT_smcb_mgmt_op(smcb) && !PINT_smcb_misc_op(smcb))
450        return 1;
451    return 0;
452}
453
454/* Function: PINT_smcb_set_complete
455   Params: pointer to an smcb pointer
456   Returns: nothing
457   Synopsis: sets op_terminate on existing smcb
458 */
459void PINT_smcb_set_complete(struct PINT_smcb *smcb)
460{
461    smcb->op_terminate = 1;
462}
463
464/* Function: PINT_smcb_complete
465   Params: pointer to an smcb pointer
466   Returns: op (int)
467   Synopsis: returns the op_terminate currently set in the smcb
468 */
469int PINT_smcb_complete(struct PINT_smcb *smcb)
470{
471    return smcb->op_terminate;
472}
473
474/* Function: PINT_smcb_set_cancelled
475   Params: pointer to an smcb pointer
476   Returns: nothing
477   Synopsis: sets op_cancelled on existing smcb
478 */
479void PINT_smcb_set_cancelled(struct PINT_smcb *smcb)
480{
481    smcb->op_cancelled = 1;
482}
483
484/* Function: PINT_smcb_cancelled
485   Params: pointer to an smcb pointer
486   Returns: op (int)
487   Synopsis: returns the op_cancelled currently set in the smcb
488 */
489int PINT_smcb_cancelled(struct PINT_smcb *smcb)
490{
491    return smcb->op_cancelled;
492}
493
494/* Function: PINT_smcb_alloc
495   Params: pointer to an smcb pointer, an op code (int), size of frame
496            (int), pinter to function to locate SM
497   Returns: nothing, but fills in pointer argument
498   Synopsis: this allocates an smcb struct, including its frame stack
499             and sets the op code so you can start the state machine
500 */
501int PINT_smcb_alloc(
502        struct PINT_smcb **smcb,
503        int op,
504        int frame_size,
505        struct PINT_state_machine_s *(*getmach)(int),
506        int (*term_fn)(struct PINT_smcb *, job_status_s *),
507        job_context_id context_id)
508{
509    *smcb = (struct PINT_smcb *)malloc(sizeof(struct PINT_smcb));
510    if (!(*smcb))
511    {
512        return -PVFS_ENOMEM;
513    }
514    /* zero out all members */
515    memset(*smcb, 0, sizeof(struct PINT_smcb));
516
517    INIT_QLIST_HEAD(&(*smcb)->frames);
518    (*smcb)->base_frame = -1; /* no frames yet */
519    (*smcb)->frame_count = 0;
520
521    /* if frame_size given, allocate a frame */
522    if (frame_size > 0)
523    {
524        void *new_frame = malloc(frame_size);
525        if (!new_frame)
526        {
527            free(*smcb);
528            *smcb = NULL;
529            return -PVFS_ENOMEM;
530        }
531        /* zero out all members */
532        memset(new_frame, 0, frame_size);
533        PINT_sm_push_frame(*smcb, 0, new_frame);
534        (*smcb)->base_frame = 0;
535    }
536    (*smcb)->op = op;
537    (*smcb)->op_get_state_machine = getmach;
538    (*smcb)->terminate_fn = term_fn;
539    (*smcb)->context = context_id;
540    /* if a getmach given, lookup state machine */
541    if (getmach)
542        return PINT_state_machine_locate(*smcb);
543    return 0; /* success */
544}
545
546/* Function: PINT_smcb_free
547 * Params: pointer to an smcb pointer
548 * Returns: nothing, but sets the pointer to NULL
549 * Synopsis: this frees an smcb struct, including
550 * anything on the frame stack with a zero task_id
551 */
552void PINT_smcb_free(struct PINT_smcb *smcb)
553{
554    struct PINT_frame_s *frame_entry, *tmp;
555    assert(smcb);
556    qlist_for_each_entry_safe(frame_entry, tmp, &smcb->frames, link)
557    {
558        if (frame_entry->frame && frame_entry->task_id == 0)
559        {
560            /* only free if task_id is 0 */
561            free(frame_entry->frame);
562        }
563        qlist_del(&frame_entry->link);
564        free(frame_entry);
565    }
566    free(smcb);
567}
568
569/* Function: PINT_pop_state
570 * Params: pointer to an smcb pointer
571 * Returns:
572 * Synopsis: pops a SM pointer off of a stack for
573 *      implementing nested SMs - called by the
574 *      "next" routine above
575 */
576static struct PINT_state_s *PINT_pop_state(struct PINT_smcb *smcb)
577{
578    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
579            "[SM pop_state]: (%p) op-id: %d stk-ptr: %d base-frm: %d\n",
580            smcb, smcb->op, smcb->stackptr, smcb->base_frame);
581   
582    if(smcb->stackptr == 0)
583    {
584        /* this is not an error, we terminate if we return NULL */
585        /* this is return from main */
586        return NULL;
587    }
588
589    smcb->stackptr--;
590    smcb->base_frame = smcb->state_stack[smcb->stackptr].prev_base_frame;
591    return smcb->state_stack[smcb->stackptr].state;
592}
593
594/* Function: PINT_push_state
595 * Params: pointer to an smcb pointer
596 * Returns:
597 * Synopsis: pushes a SM pointer into a stack for
598 *      implementing nested SMs - called by the
599 *      "next" routine above
600 */
601static void PINT_push_state(struct PINT_smcb *smcb,
602                            struct PINT_state_s *p)
603{
604    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
605            "[SM push_state]: (%p) op-id: %d stk-ptr: %d base-frm: %d\n",
606            smcb, smcb->op, smcb->stackptr, smcb->base_frame);
607
608    assert(smcb->stackptr < PINT_STATE_STACK_SIZE);
609
610    smcb->state_stack[smcb->stackptr].prev_base_frame = smcb->base_frame;
611    smcb->base_frame = smcb->frame_count - 1;
612    smcb->state_stack[smcb->stackptr].state = p;
613    smcb->stackptr++;
614}
615
616/* Function: PINT_sm_frame
617 * Params: pointer to smcb, stack index
618 * Returns: pointer to frame
619 * Synopsis: returns a frame off of the frame stack
620 * An index of 0 indicates the base frame specified in the SMCB
621 * A +'ve index indicates a frame pushed by this SM
622 * A -'ve index indicates a frame from a prior SM
623 * smcb->frames.next is the top of stack
624 * smcb->frames.prev is the bottom of stack
625 */
626void *PINT_sm_frame(struct PINT_smcb *smcb, int index)
627{
628    struct PINT_frame_s *frame_entry;
629    struct qlist_head *prev;
630    int target = smcb->base_frame + index;
631
632    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
633            "[SM frame get]: (%p) op-id: %d index: %d base-frm: %d\n",
634            smcb, smcb->op, index, smcb->base_frame);
635    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
636                 "children_running=%d\n", smcb->children_running); /* sson */
637
638    if(qlist_empty(&smcb->frames))
639    {
640        gossip_err("FRAME GET smcb %p index %d target %d -> List empty\n",
641                     smcb, index, target);
642        return NULL;
643    }
644    else
645    {
646        /* target should be 0 .. frame_count-1 now */
647        if (target < 0 || target >= smcb->frame_count)
648        {
649            gossip_err("FRAME GET smcb %p index %d target %d -> Out of range\n",
650                     smcb, index, target);
651            return NULL;
652        }
653        prev = smcb->frames.prev;
654        while(target)
655        {
656            target--;
657            prev = prev->prev;
658        }
659        frame_entry = qlist_entry(prev, struct PINT_frame_s, link);
660        gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "returned frame=%p\n",
661                     frame_entry->frame); /* sson */
662        return frame_entry->frame;
663    }
664}
665
666/* Function: PINT_sm_push_frame
667 * Params: pointer to smcb, void pointer for new frame
668 * Returns:
669 * Synopsis: pushes a new frame pointer onto the frame_stack
670 */
671int PINT_sm_push_frame(struct PINT_smcb *smcb, int task_id, void *frame_p)
672{
673    struct PINT_frame_s *newframe;
674    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
675                 "[SM Frame PUSH]: (%p) frame: %p\n",
676                 smcb, frame_p);
677    newframe = malloc(sizeof(struct PINT_frame_s));
678    if(!newframe)
679    {
680        return -PVFS_ENOMEM;
681    }
682    newframe->task_id = task_id;
683    newframe->frame = frame_p;
684    newframe->error = 0;
685    qlist_add(&newframe->link, &smcb->frames);
686    smcb->frame_count++;
687    return 0;
688}
689
690/* Function: PINT_sm_pop_frame
691 * Params: smcb - pointer to an smcb pointer
692 *         task_id - the task id of this frame
693 *         error_code - the frame's error if there was one.
694 *         remaining - count of remaining frames on the smcb.
695 * Returns: frame pointer
696 * Synopsis: pops a frame pointer from the frame_stack and returns it
697 */
698void *PINT_sm_pop_frame(struct PINT_smcb *smcb,
699                        int *task_id,
700                        int *error_code,
701                        int *remaining)
702{
703    struct PINT_frame_s *frame_entry;
704    void *frame;
705
706    if(qlist_empty(&smcb->frames))
707    {
708        return NULL;
709    }
710
711    frame_entry = qlist_entry(smcb->frames.next, struct PINT_frame_s, link);
712    qlist_del(smcb->frames.next);
713    smcb->frame_count--;
714
715    if(remaining)
716    {
717        *remaining = smcb->frame_count;
718    }
719
720    frame = frame_entry->frame;
721    *error_code = frame_entry->error;
722    *task_id = frame_entry->task_id;
723
724    free(frame_entry);
725
726    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG,
727            "[SM Frame POP]: (%p) frame: %p\n",
728            smcb, frame);
729    return frame;
730}
731
732/* Function: PINT_sm_task_map
733 * Params: smcb and an integer task_id
734 * Returns: The state machine a new child state should execute
735 * Synopsis: Uses the task_id and task jump table from the SM
736 *      code to decide which SM a new child should run.  Called
737 *      by the start_child_frames function
738 */
739static struct PINT_state_s *PINT_sm_task_map(struct PINT_smcb *smcb, int task_id)
740{
741    struct PINT_pjmp_tbl_s *pjmptbl;
742    int i;
743
744    pjmptbl = smcb->current_state->pjtbl;
745    for (i = 0; ; i++)
746    { if (pjmptbl[i].return_value == task_id ||
747                pjmptbl[i].return_value == -1)
748            return pjmptbl[i].state_machine->first_state;
749    }
750}
751
752static int child_sm_frame_terminate(struct PINT_smcb * smcb, job_status_s * js_p)
753{
754    PINT_smcb_free(smcb);
755    return 0;
756}
757
758/* Function: PINT_sm_start_child_frames
759 * Params: pointer to an smcb pointer and pointer to count of children
760 *      started
761 * Returns: number of children started
762 * Synopsis: This starts all the enw child SMs based on the frame_stack
763 *      This is called by the invoke function above which expects the
764 *      number of children to be returned to decide if the state is
765 *      deferred or not.
766 */
767static void PINT_sm_start_child_frames(struct PINT_smcb *smcb, int* children_started)
768{
769    int retval;
770    struct PINT_smcb *new_sm;
771    job_status_s r;
772    struct PINT_frame_s *f;
773    void *my_frame;
774
775    assert(smcb);
776
777    memset(&r, 0, sizeof(job_status_s));
778
779    *children_started = 0;
780
781    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "%s: called\n", __func__); /* sson */
782    my_frame = PINT_sm_frame(smcb, PINT_FRAME_CURRENT);
783    gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "%s: my_frame=%p\n", __func__, my_frame); /* sson */
784    /* Iterate once up front to determine how many children we are going to
785     * run.  This has to be set before starting any children, otherwise if
786     * the first one immediately completes it will mistakenly believe it is
787     * the last one and signal the parent.
788     */
789    qlist_for_each_entry(f, &smcb->frames, link)
790    {
791        gossip_debug(GOSSIP_STATE_MACHINE_DEBUG, "f->frame=%p\n", f->frame);
792        /* run from TOS until the parent frame */
793        if(f->frame == my_frame)
794        {
795            break;
796        }
797        /* increment parent's counter */
798        smcb->children_running++;
799    }
800
801    /* let the caller know how many children are being started; it won't be
802     * able to tell from the running_count because they may all immediately
803     * complete before we leave this function.
804     */
805    *children_started = smcb->children_running;
806
807    qlist_for_each_entry(f, &smcb->frames, link)
808    {
809        /* run from TOS until the parent frame */
810        if(f->frame == my_frame)
811        {
812            break;
813        }
814        /* allocate smcb */
815        PINT_smcb_alloc(&new_sm, smcb->op, 0, NULL,
816                child_sm_frame_terminate, smcb->context);
817        /* set parent smcb pointer */
818        new_sm->parent_smcb = smcb;
819        /* assign frame */
820        PINT_sm_push_frame(new_sm, f->task_id, f->frame);
821        /* locate SM to run */
822        new_sm->current_state = PINT_sm_task_map(smcb, f->task_id);
823        /* invoke SM */
824        retval = PINT_state_machine_start(new_sm, &r);
825        if(retval < 0)
826        {
827            gossip_err("PJMP child state machine failed to start.\n");
828        }
829    }
830}
831
832char * PINT_sm_action_string[3] =
833{
834    "DEFERRED",
835    "COMPLETE",
836    "TERMINATE"
837};
838
839/*
840 * Local variables:
841 *  c-indent-level: 4
842 *  c-basic-offset: 4
843 * End:
844 *
845 * vim: ts=8 sts=4 sw=4 expandtab
846 */
847
848#endif
849
Note: See TracBrowser for help on using the browser.