<'
# Copyright (c) 2011-2015, 2017, 2020, 2022, 2025-2026 LAAS/CNRS
# All rights reserved.
#
# Redistribution and use  in source  and binary  forms,  with or without
# modification, are permitted provided that the following conditions are
# met:
#
#   1. Redistributions of  source  code must retain the  above copyright
#      notice and this list of conditions.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice and  this list of  conditions in the  documentation and/or
#      other materials provided with the distribution.
#
#                                      Anthony Mallet on Fri Mar  2 2012
#

args components
lang c
'>
#include <assert.h>
#include <stdio.h>

#include "component.h"
#include "activity.h"
#include "prof.h"
#include "typeops-c.h"

<'foreach component $components {'>
<'  set comp [$component name]'>

/* <"[--- $comp =========================================================]"> */

/* codels */
<"[language mapping [$component codels] off]">

<'  foreach s [$component services] {'>
/* <"[--- [$s kind] [$s name] control codels ----------------------------]"> */

genom_event
genom_<"$comp">_svc_<"[$s name]">_controlcb(
  struct genom_<"$comp">_activity_<"[$s name]"> *a,
  struct genom_<"$comp">_component *self)
{
  const char *codel = "<internal>";
  genom_event s = genom_ok;
  int delay;
<'    if {[llength [$s codels validate simple]]} {'>
  genom_prof_decl(event);
<'    }'>

  assert(a->h.status == ACT_INIT);

  /* check allowance (shutting down, before/after statements) */
  if (self->control.shutdown) {
    s = genom_disallowed(&self->control.context);
    goto done;
  }
<'    foreach other [$s after] {'>
  if (!self->control.run_map[<"$comp">_<"[$other name]">_SVCID]) {
    s = genom_disallowed(&self->control.context);
    goto done;
  }
<'    }'>
<'    foreach other [$s before] {'>
  if (self->control.run_map[<"$comp">_<"[$other name]">_SVCID]) {
    s = genom_disallowed(&self->control.context);
    goto done;
  }
<'    }'>

  /* call validate codel */
<'    foreach validate [$s validate] {'>
  codel = "<"[$validate cname]">";
  genom_prof_enter(&event);
  genom_take_resource(
    self,
<'      foreach m [$validate mutex] {'>
<'        if {[$m class] ne "codel"} continue'>
<'        if {[catch {$m task} t]} continue'>
    self->resources.task_<"[$t name]"> == <"[$m cname]"> ||
<'      }'>
    self->resources.all,

    self->resources.control = <"[$validate cname]">);
<'
      # build parameter list
      set plist [list]
      foreach p [$validate parameters] {
        switch -- [$p src] {
          ids		{ set m "self->ids[$p member]" }
          local		{ set m "a->params.[$p name]" }
          default	{ error "unsupported codel parameter source" }
        }
        switch -- [$p dir] {
          in		{ lappend plist [[$p type] pass value $m] }
          inout - out	{ lappend plist [[$p type] pass reference $m] }
          default	{ error "unsupported direction" }
        }
      }
      lappend plist &self->control.context
'>
  genom_prof_start(&event);
  s = <"[$validate invoke $plist]">;
  genom_give_resource(self, self->resources.control = NULL);
  genom_prof_leave(&event);

  genom_prof_collect(
    &event, self->instance,
    "<control>", "<"[$s name]">", "<"[$validate cname]">", "validate", s);
  genom_log_debug(
    self->instance, "service <"[$s name]"> validation returned %s", s?s:"ok");
  if (s) goto done;
<'    }'>

  /* interrupt incompatible activities */
  delay = genom_<"$comp">_interrupt_other(&a->h, self);

  /* copy ids parameters to ids (attributes) */
<'    foreach p [$s parameters] {'>
<'      if {[$p src] == "ids"} {'>
  genom_take_resource(
    self,
<'        foreach m [$s mutex] {'>
<'          if {[$m class] ne "codel"} continue'>
<'          if {[catch {$m task} t]} continue'>
    self->resources.task_<"[$t name]"> == <"[$m cname]"> ||
<'        }'>
    self->resources.all,

    self->resources.control = genom_<"$comp">_svc_<"[$s name]">_controlcb);
<'        foreach p [$s parameters in inout] {'>
<'          if {[$p src] == "ids"} {'>
  genom_tcopy_<"[[$p type] mangle]">(
    <"[[$p type] pass reference self->ids[$p member]]">,
    <"[[$p type] pass value a->params.[$p name]]">);
<'          }'>
<'        }'>
<'        foreach p [$s parameters out] {'>
<'          if {[$p src] == "ids"} {'>
  genom_tcopy_<"[[$p type] mangle]">(
    <"[[$p type] pass reference a->params.[$p name]]">,
    <"[[$p type] pass value self->ids[$p member]]">);
<'          }'>
<'        }'>
  genom_give_resource(self, self->resources.control = NULL);
<'        break'>
<'      }'>
<'    }'>

  /* call simple codels */
<'    foreach c [$s codels simple] {'>
  codel = "<"[$c cname]">";
  genom_prof_enter(&event);
  genom_take_resource(
    self,
<'      foreach m [$c mutex] {'>
<'        if {[$m class] ne "codel"} continue'>
<'        if {[catch {$m task} t]} continue'>
    self->resources.task_<"[$t name]"> == <"[$m cname]"> ||
<'      }'>
    self->resources.all,

    self->resources.control = <"[$c cname]">);
<'      # build parameter list
      set plist [list]
      foreach p [$c parameters] {
        switch -- [$p src] {
          ids		{ set m "self->ids[$p member]" }
          local		{ set m "a->params.[$p name]" }
          default	{ error "unsupported codel parameter" }
        }
        switch -- [$p dir] {
          in		{ lappend plist [[$p type] pass value $m] }
          inout - out	{ lappend plist [[$p type] pass reference $m] }
          default	{ error "unsupported direction" }
        }
      }
      lappend plist &self->control.context
'>
  genom_prof_start(&event);
  s = <"[$c invoke $plist]">;
  genom_give_resource(self, self->resources.control = NULL);

  genom_prof_leave(&event);
  genom_prof_collect(
    &event, self->instance,
    "<control>", "<"[$s name]">", "<"[$c cname]">", "codel", s);

  genom_log_debug(self->instance, "service %s codel %s returned %s",
                  "<"[$s name]">", "<"[$c name]">", s?s:"ok");
  if (s) goto done;
<'    }'>

  /* update states */
<'    if {[catch {$s task}]} {'>
  self->control.run_map[<"$comp">_<"[$s name]">_SVCID] = true;
<'    }'>
  a->h.start = !delay;
  goto done;

done:
  if (s
<'      foreach t [$s throw] {'>
      && s != <"[$t cname]">_id
<'      }'>
      ) {
    genom_unkex_detail d;
    snprintf(d.what, sizeof(d.what), "%s", s);
    genom_log_warnx(
      self->instance, "bad exception %s raised by codel %s in service %s",
      s, codel, "<"[$s name]">");
    s = genom_unkex(&d, &self->control.context);
  }

  a->h.state = s;
  if (s)
    a->h.exdetail = self->control.context.raised(NULL, &self->control.context);

  /* wake up non-periodic activities */
<'    foreach t [$component tasks] {'>
<'      if {[catch {$t period}]} {'>
  pthread_mutex_lock(&self->tasks.<"[$t name]">_.lock);
  if (!self->tasks.<"[$t name]">_.runnable) {
    self->tasks.<"[$t name]">_.runnable = 1;
    pthread_cond_broadcast(&self->tasks.<"[$t name]">_.sync);
  }
  pthread_mutex_unlock(&self->tasks.<"[$t name]">_.lock);
<'      }'>
<'    }'>

  return s;
}

<'  }'>
<'}'>
/* eof */
