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