go-hep.org/x/hep@v0.38.1/fwk/app.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package fwk
     6  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"io"
    11  	"math"
    12  	"reflect"
    13  	"runtime"
    14  	"slices"
    15  	"sort"
    16  	"time"
    17  
    18  	"go-hep.org/x/hep/fwk/fsm"
    19  )
    20  
    21  type appmgr struct {
    22  	state fsm.State
    23  	name  string
    24  
    25  	props map[string]map[string]any
    26  	dflow *dflowsvc
    27  	store *datastore
    28  	msg   msgstream
    29  
    30  	evtmax int64
    31  	nprocs int
    32  
    33  	comps   map[string]Component
    34  	tsks    []Task
    35  	svcs    []Svc
    36  	istream Task
    37  	ctxs    [2][]ctxType
    38  }
    39  
    40  // NewApp creates a (default) fwk application with (default and) sensible options.
    41  func NewApp() App {
    42  
    43  	var err error
    44  	var app *appmgr
    45  
    46  	const appname = "app"
    47  
    48  	app = &appmgr{
    49  		state: fsm.Undefined,
    50  		name:  appname,
    51  		props: make(map[string]map[string]any),
    52  		dflow: nil,
    53  		store: nil,
    54  		msg: newMsgStream(
    55  			appname,
    56  			LvlInfo,
    57  			//LvlDebug,
    58  			//LvlError,
    59  			nil,
    60  		),
    61  		evtmax: -1,
    62  		nprocs: -1,
    63  		comps:  make(map[string]Component),
    64  		tsks:   make([]Task, 0),
    65  		svcs:   make([]Svc, 0),
    66  	}
    67  
    68  	svc, err := app.New("go-hep.org/x/hep/fwk.datastore", "evtstore")
    69  	if err != nil {
    70  		app.msg.Errorf("fwk.NewApp: could not create evtstore: %w\n", err)
    71  		return nil
    72  	}
    73  	app.store = svc.(*datastore)
    74  
    75  	err = app.AddSvc(app.store)
    76  	if err != nil {
    77  		app.msg.Errorf("fwk.NewApp: could not create evtstore: %w\n", err)
    78  		return nil
    79  	}
    80  
    81  	svc, err = app.New("go-hep.org/x/hep/fwk.dflowsvc", "dataflow")
    82  	if err != nil {
    83  		app.msg.Errorf("fwk.NewApp: could not create dataflow svc: %w\n", err)
    84  		return nil
    85  	}
    86  	app.dflow = svc.(*dflowsvc)
    87  
    88  	err = app.AddSvc(app.dflow)
    89  	if err != nil {
    90  		app.msg.Errorf("fwk.NewApp: could not create dataflow svc: %w\n", err)
    91  		return nil
    92  	}
    93  
    94  	err = app.DeclProp(app, "EvtMax", &app.evtmax)
    95  	if err != nil {
    96  		app.msg.Errorf("fwk.NewApp: could not declare property 'EvtMax': %w\n", err)
    97  		return nil
    98  	}
    99  
   100  	err = app.DeclProp(app, "NProcs", &app.nprocs)
   101  	if err != nil {
   102  		app.msg.Errorf("fwk.NewApp: could not declare property 'NProcs': %w\n", err)
   103  		return nil
   104  	}
   105  
   106  	err = app.DeclProp(app, "MsgLevel", &app.msg.lvl)
   107  	if err != nil {
   108  		app.msg.Errorf("fwk.NewApp: could not declare property 'MsgLevel': %w\n", err)
   109  		return nil
   110  	}
   111  
   112  	return app
   113  }
   114  
   115  // Type returns the fully qualified type of this application
   116  func (app *appmgr) Type() string {
   117  	return "go-hep.org/x/hep/fwk.appmgr"
   118  }
   119  
   120  // Name returns the name of this application
   121  func (app *appmgr) Name() string {
   122  	return app.name
   123  }
   124  
   125  func (app *appmgr) Component(n string) Component {
   126  	c, ok := app.comps[n]
   127  	if !ok {
   128  		return nil
   129  	}
   130  	return c
   131  }
   132  
   133  func (app *appmgr) addComponent(c Component) error {
   134  	app.comps[c.Name()] = c
   135  	return nil
   136  }
   137  
   138  func (app *appmgr) HasComponent(n string) bool {
   139  	_, ok := app.comps[n]
   140  	return ok
   141  }
   142  
   143  func (app *appmgr) Components() []Component {
   144  	comps := make([]Component, 0, len(app.comps))
   145  	for _, c := range app.comps {
   146  		comps = append(comps, c)
   147  	}
   148  	return comps
   149  }
   150  
   151  func (app *appmgr) AddTask(tsk Task) error {
   152  	var err error
   153  	app.tsks = append(app.tsks, tsk)
   154  	app.comps[tsk.Name()] = tsk
   155  	return err
   156  }
   157  
   158  func (app *appmgr) DelTask(tsk Task) error {
   159  	var err error
   160  	tsks := make([]Task, 0, len(app.tsks))
   161  	for _, t := range app.tsks {
   162  		if t.Name() != tsk.Name() {
   163  			tsks = append(tsks, t)
   164  		}
   165  	}
   166  	app.tsks = tsks
   167  	return err
   168  }
   169  
   170  func (app *appmgr) HasTask(n string) bool {
   171  	for _, t := range app.tsks {
   172  		if t.Name() == n {
   173  			return true
   174  		}
   175  	}
   176  	return false
   177  }
   178  
   179  func (app *appmgr) GetTask(n string) Task {
   180  	for _, t := range app.tsks {
   181  		if t.Name() == n {
   182  			return t
   183  		}
   184  	}
   185  	return nil
   186  }
   187  
   188  func (app *appmgr) Tasks() []Task {
   189  	return app.tsks
   190  }
   191  
   192  func (app *appmgr) AddSvc(svc Svc) error {
   193  	var err error
   194  	app.svcs = append(app.svcs, svc)
   195  	app.comps[svc.Name()] = svc
   196  	return err
   197  }
   198  
   199  func (app *appmgr) DelSvc(svc Svc) error {
   200  	var err error
   201  	svcs := make([]Svc, 0, len(app.svcs))
   202  	for _, s := range app.svcs {
   203  		if s.Name() != svc.Name() {
   204  			svcs = append(svcs, s)
   205  		}
   206  	}
   207  	app.svcs = svcs
   208  	return err
   209  }
   210  
   211  func (app *appmgr) HasSvc(n string) bool {
   212  	for _, s := range app.svcs {
   213  		if s.Name() == n {
   214  			return true
   215  		}
   216  	}
   217  	return false
   218  }
   219  
   220  func (app *appmgr) GetSvc(n string) Svc {
   221  	for _, s := range app.svcs {
   222  		if s.Name() == n {
   223  			return s
   224  		}
   225  	}
   226  	return nil
   227  }
   228  
   229  func (app *appmgr) Svcs() []Svc {
   230  	return app.svcs
   231  }
   232  
   233  func (app *appmgr) DeclProp(c Component, name string, ptr any) error {
   234  	cname := c.Name()
   235  	_, ok := app.props[cname]
   236  	if !ok {
   237  		app.props[cname] = make(map[string]any)
   238  	}
   239  	switch reflect.TypeOf(ptr).Kind() {
   240  	case reflect.Ptr:
   241  		// ok
   242  	default:
   243  		return fmt.Errorf(
   244  			"fwk.DeclProp: component [%s] didn't pass a pointer for the property [%s] (type=%T)",
   245  			c.Name(),
   246  			name,
   247  			ptr,
   248  		)
   249  	}
   250  	app.props[cname][name] = ptr
   251  	return nil
   252  }
   253  
   254  func (app *appmgr) SetProp(c Component, name string, value any) error {
   255  	cname := c.Name()
   256  	m, ok := app.props[cname]
   257  	if !ok {
   258  		return fmt.Errorf(
   259  			"fwk.SetProp: component [%s] didn't declare any property",
   260  			c.Name(),
   261  		)
   262  	}
   263  
   264  	rv := reflect.ValueOf(value)
   265  	rt := rv.Type()
   266  	ptr := reflect.ValueOf(m[name])
   267  	dst := ptr.Elem().Type()
   268  	if !rt.AssignableTo(dst) {
   269  		return fmt.Errorf(
   270  			"fwk.SetProp: component [%s] has property [%s] with type [%s]. got value=%v (type=%s)",
   271  			c.Name(),
   272  			name,
   273  			dst.Name(),
   274  			value,
   275  			rt.Name(),
   276  		)
   277  	}
   278  	ptr.Elem().Set(rv)
   279  
   280  	return nil
   281  }
   282  
   283  func (app *appmgr) GetProp(c Component, name string) (any, error) {
   284  	cname := c.Name()
   285  	m, ok := app.props[cname]
   286  	if !ok {
   287  		return nil, fmt.Errorf(
   288  			"fwk.GetProp: component [%s] didn't declare any property",
   289  			c.Name(),
   290  		)
   291  	}
   292  
   293  	ptr, ok := m[name]
   294  	if !ok {
   295  		return nil, fmt.Errorf(
   296  			"fwk.GetProp: component [%s] didn't declare any property with name [%s]",
   297  			c.Name(),
   298  			name,
   299  		)
   300  	}
   301  
   302  	v := reflect.Indirect(reflect.ValueOf(ptr)).Interface()
   303  	return v, nil
   304  }
   305  
   306  func (app *appmgr) HasProp(c Component, name string) bool {
   307  	cname := c.Name()
   308  	_, ok := app.props[cname]
   309  	if !ok {
   310  		return ok
   311  	}
   312  	_, ok = app.props[cname][name]
   313  	return ok
   314  }
   315  
   316  func (app *appmgr) DeclInPort(c Component, name string, t reflect.Type) error {
   317  	if app.state < fsm.Configuring {
   318  		return fmt.Errorf(
   319  			"fwk.DeclInPort: invalid App state (%s). put the DeclInPort in Configure() of %s:%s",
   320  			app.state,
   321  			c.Type(),
   322  			c.Name(),
   323  		)
   324  	}
   325  	return app.dflow.addInNode(c.Name(), name, t)
   326  }
   327  
   328  func (app *appmgr) DeclOutPort(c Component, name string, t reflect.Type) error {
   329  	if app.state < fsm.Configuring {
   330  		return fmt.Errorf(
   331  			"fwk.DeclOutPort: invalid App state (%s). put the DeclInPort in Configure() of %s:%s",
   332  			app.state,
   333  			c.Type(),
   334  			c.Name(),
   335  		)
   336  	}
   337  	return app.dflow.addOutNode(c.Name(), name, t)
   338  }
   339  
   340  func (app *appmgr) FSMState() fsm.State {
   341  	return app.state
   342  }
   343  
   344  func (app *appmgr) Run() error {
   345  	var err error
   346  	ctx := ctxType{
   347  		id:    0,
   348  		slot:  0,
   349  		store: nil,
   350  		msg:   newMsgStream("<root>", app.msg.lvl, nil),
   351  		mgr:   app,
   352  	}
   353  
   354  	start := time.Now()
   355  	var mstart runtime.MemStats
   356  	runtime.ReadMemStats(&mstart)
   357  
   358  	if app.state == fsm.Undefined {
   359  		err = app.configure(ctx)
   360  		if err != nil {
   361  			return err
   362  		}
   363  	}
   364  
   365  	if app.state == fsm.Configured {
   366  		err = app.start(ctx)
   367  		if err != nil {
   368  			return err
   369  		}
   370  	}
   371  
   372  	if app.state == fsm.Started {
   373  		err = app.run(ctx)
   374  		if err != nil && err != io.EOF {
   375  			return err
   376  		}
   377  	}
   378  
   379  	if app.state == fsm.Running {
   380  		err = app.stop(ctx)
   381  		if err != nil {
   382  			return err
   383  		}
   384  	}
   385  
   386  	if app.state == fsm.Stopped {
   387  		err = app.shutdown(ctx)
   388  		if err != nil {
   389  			return err
   390  		}
   391  	}
   392  
   393  	app.msg.Infof("cpu: %v\n", time.Since(start))
   394  	var mdone runtime.MemStats
   395  	runtime.ReadMemStats(&mdone)
   396  
   397  	diff := func(v1, v0 uint64) int64 {
   398  		if v0 > v1 {
   399  			return -int64(v0 - v1)
   400  		}
   401  		return int64(v1 - v0)
   402  	}
   403  
   404  	app.msg.Infof("mem: alloc:     %10d kB\n", diff(mdone.Alloc, mstart.Alloc)/1024)
   405  	app.msg.Infof("mem: tot-alloc: %10d kB\n", diff(mdone.TotalAlloc, mstart.TotalAlloc)/1024)
   406  	app.msg.Infof("mem: n-mallocs: %10d\n", diff(mdone.Mallocs, mstart.Mallocs))
   407  	app.msg.Infof("mem: n-frees:   %10d\n", diff(mdone.Frees, mstart.Frees))
   408  	app.msg.Infof("mem: gc-pauses: %10d ms\n", diff(mdone.PauseTotalNs, mstart.PauseTotalNs)/1000000)
   409  
   410  	return err
   411  }
   412  
   413  func (app *appmgr) Scripter() Scripter {
   414  	return &irunner{app}
   415  }
   416  
   417  func (app *appmgr) configure(ctx Context) error {
   418  	var err error
   419  	defer app.msg.flush()
   420  	app.msg.Debugf("configure...\n")
   421  	app.state = fsm.Configuring
   422  
   423  	if app.evtmax == -1 {
   424  		app.evtmax = math.MaxInt64
   425  	}
   426  
   427  	if app.nprocs < 0 {
   428  		app.nprocs = runtime.NumCPU()
   429  	}
   430  
   431  	tsks := make([]ctxType, len(app.tsks))
   432  	for j, tsk := range app.tsks {
   433  		tsks[j] = ctxType{
   434  			id:    -1,
   435  			slot:  0,
   436  			store: app.store,
   437  			msg:   newMsgStream(tsk.Name(), app.msg.lvl, nil),
   438  			mgr:   app,
   439  		}
   440  	}
   441  
   442  	svcs := make([]ctxType, len(app.svcs))
   443  	for j, svc := range app.svcs {
   444  		svcs[j] = ctxType{
   445  			id:    -1,
   446  			slot:  0,
   447  			store: app.store,
   448  			msg:   newMsgStream(svc.Name(), app.msg.lvl, nil),
   449  			mgr:   app,
   450  		}
   451  	}
   452  
   453  	for i, svc := range app.svcs {
   454  		app.msg.Debugf("configuring [%s]...\n", svc.Name())
   455  		cfg, ok := svc.(Configurer)
   456  		if !ok {
   457  			continue
   458  		}
   459  		err = cfg.Configure(svcs[i])
   460  		if err != nil {
   461  			return err
   462  		}
   463  	}
   464  
   465  	for i, tsk := range app.tsks {
   466  		app.msg.Debugf("configuring [%s]...\n", tsk.Name())
   467  		cfg, ok := tsk.(Configurer)
   468  		if !ok {
   469  			continue
   470  		}
   471  		err = cfg.Configure(tsks[i])
   472  		if err != nil {
   473  			return err
   474  		}
   475  	}
   476  
   477  	err = app.printDataFlow()
   478  	if err != nil {
   479  		return err
   480  	}
   481  
   482  	app.ctxs[0] = tsks
   483  	app.ctxs[1] = svcs
   484  	app.state = fsm.Configured
   485  	app.msg.Debugf("configure... [done]\n")
   486  	return err
   487  }
   488  
   489  func (app *appmgr) start(ctx Context) error {
   490  	var err error
   491  	defer app.msg.flush()
   492  	app.state = fsm.Starting
   493  	for i, svc := range app.svcs {
   494  		app.msg.Debugf("starting [%s]...\n", svc.Name())
   495  		err = svc.StartSvc(app.ctxs[1][i])
   496  		if err != nil {
   497  			return err
   498  		}
   499  	}
   500  
   501  	for i, tsk := range app.tsks {
   502  		app.msg.Debugf("starting [%s]...\n", tsk.Name())
   503  		err = tsk.StartTask(app.ctxs[0][i])
   504  		if err != nil {
   505  			return err
   506  		}
   507  	}
   508  
   509  	app.state = fsm.Started
   510  	return err
   511  }
   512  
   513  func (app *appmgr) run(ctx Context) error {
   514  	var err error
   515  	defer app.msg.flush()
   516  	app.state = fsm.Running
   517  
   518  	maxprocs := runtime.GOMAXPROCS(app.nprocs)
   519  
   520  	switch app.nprocs {
   521  	case 0:
   522  		err = app.runSequential(ctx)
   523  	default:
   524  		err = app.runConcurrent(ctx)
   525  	}
   526  
   527  	runtime.GOMAXPROCS(maxprocs)
   528  
   529  	return err
   530  }
   531  
   532  func (app *appmgr) runSequential(ctx Context) error {
   533  	var err error
   534  
   535  	runctx, runCancel := context.WithCancel(context.Background())
   536  	defer runCancel()
   537  
   538  	keys := app.dflow.keys()
   539  	ctxs := make([]ctxType, len(app.tsks))
   540  	store := *app.store
   541  	for j, tsk := range app.tsks {
   542  		ctxs[j] = ctxType{
   543  			id:    -1,
   544  			slot:  0,
   545  			store: &store,
   546  			msg:   newMsgStream(tsk.Name(), app.msg.lvl, nil),
   547  			mgr:   app,
   548  		}
   549  	}
   550  
   551  	ictrl, err := app.startInputStream()
   552  	if err != nil {
   553  		return err
   554  	}
   555  	defer close(ictrl.Quit)
   556  
   557  	octrl, err := app.startOutputStreams()
   558  	if err != nil {
   559  		return err
   560  	}
   561  
   562  	defer close(octrl.Quit)
   563  
   564  	for ievt := int64(0); ievt < app.evtmax; ievt++ {
   565  		evtctx, evtCancel := context.WithCancel(runctx)
   566  
   567  		app.msg.Infof(">>> running evt=%d...\n", ievt)
   568  		err = store.reset(keys)
   569  		if err != nil {
   570  			evtCancel()
   571  			return err
   572  		}
   573  		err = app.istream.Process(ctxs[0])
   574  		if err != nil {
   575  			evtCancel()
   576  			store.close()
   577  			app.msg.flush()
   578  			return err
   579  		}
   580  		run := taskrunner{
   581  			ievt:   ievt,
   582  			errc:   make(chan error, len(app.tsks)),
   583  			evtctx: evtctx,
   584  		}
   585  		for i, tsk := range app.tsks {
   586  			go run.run(i, ctxs[i], tsk)
   587  		}
   588  		ndone := 0
   589  	errloop:
   590  		for err = range run.errc {
   591  			ndone++
   592  			if err != nil {
   593  				evtCancel()
   594  				store.close()
   595  				app.msg.flush()
   596  				return err
   597  			}
   598  			if ndone == len(app.tsks) {
   599  				break errloop
   600  			}
   601  		}
   602  		evtCancel()
   603  		store.close()
   604  		app.msg.flush()
   605  	}
   606  
   607  	return err
   608  }
   609  
   610  func (app *appmgr) runConcurrent(ctx Context) error {
   611  	var err error
   612  
   613  	runctx, runCancel := context.WithCancel(context.Background())
   614  	defer runCancel()
   615  
   616  	ctrl := workercontrol{
   617  		evts:   make(chan ctxType, 2*app.nprocs),
   618  		done:   make(chan struct{}),
   619  		errc:   make(chan error),
   620  		runctx: runctx,
   621  	}
   622  
   623  	istream, err := app.startInputStream()
   624  	if err != nil {
   625  		return err
   626  	}
   627  	defer close(istream.Quit)
   628  
   629  	ostream, err := app.startOutputStreams()
   630  	if err != nil {
   631  		return err
   632  	}
   633  	defer close(ostream.Quit)
   634  
   635  	workers := make([]worker, app.nprocs)
   636  	for i := range app.nprocs {
   637  		workers[i] = *newWorker(i, app, &ctrl)
   638  	}
   639  
   640  	go func() {
   641  		keys := app.dflow.keys()
   642  		msg := newMsgStream(app.istream.Name(), app.msg.lvl, nil)
   643  		for ievt := int64(0); ievt < app.evtmax; ievt++ {
   644  			evtctx, evtCancel := context.WithCancel(runctx)
   645  			store := *app.store
   646  			store.store = make(map[string]achan, len(keys))
   647  			err := store.reset(keys)
   648  			if err != nil {
   649  				evtCancel()
   650  				close(ctrl.evts)
   651  				ctrl.errc <- err
   652  				return
   653  			}
   654  			ctx := ctxType{
   655  				id:    ievt,
   656  				slot:  0,
   657  				store: &store,
   658  				msg:   msg,
   659  				mgr:   nil, // nobody's supposed to access mgr's state during event-loop
   660  				ctx:   evtctx,
   661  			}
   662  
   663  			err = app.istream.Process(ctx)
   664  			if err != nil {
   665  				if err != io.EOF {
   666  					ctrl.errc <- err
   667  				}
   668  				close(ctrl.evts)
   669  				evtCancel()
   670  				return
   671  			}
   672  			ctrl.evts <- ctx
   673  			evtCancel()
   674  		}
   675  		close(ctrl.evts)
   676  	}()
   677  
   678  	ndone := 0
   679  ctrl:
   680  	for {
   681  		select {
   682  		case eworker, ok := <-ctrl.errc:
   683  			if !ok {
   684  				continue
   685  			}
   686  			if eworker != nil && err == nil {
   687  				// only record first error.
   688  				// FIXME(sbinet) record all of them (errstack)
   689  				err = eworker
   690  			}
   691  
   692  		case <-runctx.Done():
   693  			return runctx.Err()
   694  
   695  		case <-ctrl.done:
   696  			ndone++
   697  			app.msg.Infof("workers done: %d/%d\n", ndone, app.nprocs)
   698  			if ndone == len(workers) {
   699  				break ctrl
   700  			}
   701  		}
   702  	}
   703  
   704  	return err
   705  }
   706  
   707  func (app *appmgr) startInputStream() (StreamControl, error) {
   708  	var err error
   709  
   710  	ctrl := StreamControl{
   711  		Ctx:  make(chan Context),
   712  		Err:  make(chan error), // FIXME: impl. back-pressure
   713  		Quit: make(chan struct{}),
   714  	}
   715  
   716  	idx := -1
   717  	inputs := make([]*InputStream, 0, len(app.tsks))
   718  
   719  	// collect input streams
   720  	for i, tsk := range app.tsks {
   721  		in, ok := tsk.(*InputStream)
   722  		if !ok {
   723  			continue
   724  		}
   725  		inputs = append(inputs, in)
   726  		idx = i
   727  	}
   728  
   729  	switch len(inputs) {
   730  	case 0:
   731  		// create an event "pumper"
   732  		tsk := &inputStream{NewTask("fwk.inputStream", "app-evtloop", app)}
   733  		app.istream = tsk
   734  	case 1:
   735  		app.istream = inputs[0]
   736  		app.tsks = slices.Delete(app.tsks, idx, idx+1)
   737  		err := inputs[0].connect(ctrl)
   738  		if err != nil {
   739  			return ctrl, err
   740  		}
   741  
   742  	default:
   743  		return ctrl, fmt.Errorf("found more than one InputStream! (n=%d)", len(inputs))
   744  	}
   745  
   746  	return ctrl, err
   747  }
   748  
   749  func (app *appmgr) startOutputStreams() (StreamControl, error) {
   750  	var err error
   751  
   752  	ctrl := StreamControl{
   753  		Ctx:  make(chan Context),
   754  		Err:  make(chan error), // FIXME: impl. back-pressure
   755  		Quit: make(chan struct{}),
   756  	}
   757  
   758  	// start output streams
   759  	for _, tsk := range app.tsks {
   760  		in, ok := tsk.(*OutputStream)
   761  		if !ok {
   762  			continue
   763  		}
   764  		err = in.connect(ctrl)
   765  		if err != nil {
   766  			return ctrl, err
   767  		}
   768  	}
   769  
   770  	return ctrl, err
   771  }
   772  
   773  func (app *appmgr) stop(ctx Context) error {
   774  	var err error
   775  	defer app.msg.flush()
   776  	app.state = fsm.Stopping
   777  
   778  	if app.istream != nil {
   779  		err = app.istream.StopTask(ctx)
   780  		if err != nil {
   781  			return err
   782  		}
   783  	}
   784  
   785  	for i, tsk := range app.tsks {
   786  		err = tsk.StopTask(app.ctxs[0][i])
   787  		if err != nil {
   788  			return err
   789  		}
   790  	}
   791  
   792  	for i, svc := range app.svcs {
   793  		err = svc.StopSvc(app.ctxs[1][i])
   794  		if err != nil {
   795  			return err
   796  		}
   797  	}
   798  
   799  	app.state = fsm.Stopped
   800  	return err
   801  }
   802  
   803  func (app *appmgr) shutdown(ctx Context) error {
   804  	var err error
   805  	defer app.msg.flush()
   806  	app.comps = nil
   807  	app.tsks = nil
   808  	app.svcs = nil
   809  	app.state = fsm.Offline
   810  
   811  	app.props = nil
   812  	app.dflow = nil
   813  	app.store = nil
   814  
   815  	return err
   816  }
   817  
   818  func (app *appmgr) Msg() MsgStream {
   819  	return app.msg
   820  }
   821  
   822  func (app *appmgr) printDataFlow() error {
   823  	var err error
   824  
   825  	app.msg.Debugf(">>> --- [data flow] --- nodes...\n")
   826  	for tsk, node := range app.dflow.nodes {
   827  		app.msg.Debugf(">>> ---[%s]---\n", tsk)
   828  		app.msg.Debugf("    in:  %v\n", node.in)
   829  		app.msg.Debugf("    out: %v\n", node.out)
   830  	}
   831  
   832  	app.msg.Debugf(">>> --- [data flow] --- edges...\n")
   833  	edges := make([]string, 0, len(app.dflow.edges))
   834  	for n := range app.dflow.edges {
   835  		edges = append(edges, n)
   836  	}
   837  	sort.Strings(edges)
   838  	app.msg.Debugf(" edges: %v\n", edges)
   839  
   840  	return err
   841  }
   842  
   843  func init() {
   844  	Register(
   845  		reflect.TypeOf(appmgr{}),
   846  		func(t, name string, mgr App) (Component, error) {
   847  			app := NewApp().(*appmgr)
   848  			app.name = name
   849  			return app, nil
   850  		},
   851  	)
   852  }
   853  
   854  // EOF