github.com/ergo-services/ergo@v1.999.224/node/core.go (about) 1 package node 2 3 import ( 4 "context" 5 "crypto/rsa" 6 "fmt" 7 "runtime" 8 "strings" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/ergo-services/ergo/etf" 14 "github.com/ergo-services/ergo/gen" 15 "github.com/ergo-services/ergo/lib" 16 ) 17 18 var ( 19 startPID = uint64(1000) 20 startUniqID = uint64(time.Now().UnixNano()) 21 22 corePID = etf.Pid{} 23 ) 24 25 type core struct { 26 monitorInternal 27 networkInternal 28 29 ctx context.Context 30 stop context.CancelFunc 31 32 env map[gen.EnvKey]interface{} 33 mutexEnv sync.RWMutex 34 35 compression Compression 36 37 nextPID uint64 38 uniqID uint64 39 nodename string 40 creation uint32 41 42 names map[string]etf.Pid 43 mutexNames sync.RWMutex 44 aliases map[etf.Alias]*process 45 mutexAliases sync.RWMutex 46 processes map[uint64]*process 47 mutexProcesses sync.RWMutex 48 49 behaviors map[string]map[string]gen.RegisteredBehavior 50 mutexBehaviors sync.Mutex 51 } 52 53 type coreInternal interface { 54 gen.Core 55 CoreRouter 56 57 // core environment 58 ListEnv() map[gen.EnvKey]interface{} 59 SetEnv(name gen.EnvKey, value interface{}) 60 Env(name gen.EnvKey) interface{} 61 62 monitorInternal 63 networkInternal 64 65 spawn(name string, opts processOptions, behavior gen.ProcessBehavior, args ...etf.Term) (gen.Process, error) 66 67 registerName(name string, pid etf.Pid) error 68 unregisterName(name string) error 69 70 newAlias(p *process) (etf.Alias, error) 71 deleteAlias(owner *process, alias etf.Alias) error 72 73 coreNodeName() string 74 coreStop() 75 coreUptime() int64 76 coreIsAlive() bool 77 78 coreWait() 79 coreWaitWithTimeout(d time.Duration) error 80 81 monitorStats() internalMonitorStats 82 networkStats() internalNetworkStats 83 coreStats() internalCoreStats 84 } 85 86 type internalCoreStats struct { 87 totalProcesses uint64 88 totalReferences uint64 89 processes int 90 aliases int 91 names int 92 } 93 94 type coreRouterInternal interface { 95 CoreRouter 96 MakeRef() etf.Ref 97 98 ProcessByPid(pid etf.Pid) gen.Process 99 ProcessByName(name string) gen.Process 100 ProcessByAlias(alias etf.Alias) gen.Process 101 102 processByPid(pid etf.Pid) *process 103 getConnection(nodename string) (ConnectionInterface, error) 104 sendEvent(pid etf.Pid, event gen.Event, message gen.EventMessage) error 105 } 106 107 // transit proxy session 108 type proxyTransitSession struct { 109 a ConnectionInterface 110 b ConnectionInterface 111 } 112 113 type proxyConnectRequest struct { 114 privateKey *rsa.PrivateKey 115 request ProxyConnectRequest 116 connection chan ConnectionInterface 117 cancel chan ProxyConnectCancel 118 } 119 120 func newCore(ctx context.Context, nodename string, cookie string, options Options) (coreInternal, error) { 121 if options.Compression.Level < 1 || options.Compression.Level > 9 { 122 options.Compression.Level = DefaultCompressionLevel 123 } 124 if options.Compression.Threshold < DefaultCompressionThreshold { 125 options.Compression.Threshold = DefaultCompressionThreshold 126 } 127 c := &core{ 128 env: options.Env, 129 nextPID: startPID, 130 uniqID: startUniqID, 131 // keep node to get the process to access to the node's methods 132 nodename: nodename, 133 compression: options.Compression, 134 creation: options.Creation, 135 names: make(map[string]etf.Pid), 136 aliases: make(map[etf.Alias]*process), 137 processes: make(map[uint64]*process), 138 behaviors: make(map[string]map[string]gen.RegisteredBehavior), 139 } 140 141 corePID = etf.Pid{ 142 Node: etf.Atom(c.nodename), 143 ID: 1, 144 Creation: c.creation, 145 } 146 147 corectx, corestop := context.WithCancel(ctx) 148 c.stop = corestop 149 c.ctx = context.WithValue(corectx, c, c) 150 151 c.monitorInternal = newMonitor(nodename, coreRouterInternal(c)) 152 network, err := newNetwork(c.ctx, nodename, cookie, options, coreRouterInternal(c)) 153 if err != nil { 154 corestop() 155 return nil, err 156 } 157 c.networkInternal = network 158 159 c.registerEvent(corePID, EventNetwork, []gen.EventMessage{MessageEventNetwork{}}) 160 161 return c, nil 162 } 163 164 func (c *core) coreNodeName() string { 165 return c.nodename 166 } 167 168 func (c *core) coreStop() { 169 c.stop() 170 c.stopNetwork() 171 } 172 173 func (c *core) coreUptime() int64 { 174 return time.Now().Unix() - int64(c.creation) 175 } 176 177 func (c *core) coreWait() { 178 <-c.ctx.Done() 179 } 180 181 // WaitWithTimeout waits until node stopped. Return ErrTimeout 182 // if given timeout is exceeded 183 func (c *core) coreWaitWithTimeout(d time.Duration) error { 184 timer := time.NewTimer(d) 185 defer timer.Stop() 186 187 select { 188 case <-timer.C: 189 return lib.ErrTimeout 190 case <-c.ctx.Done(): 191 return nil 192 } 193 } 194 195 // IsAlive returns true if node is running 196 func (c *core) coreIsAlive() bool { 197 return c.ctx.Err() == nil 198 } 199 200 func (c *core) newPID() etf.Pid { 201 // http://erlang.org/doc/apps/erts/erl_ext_dist.html#pid_ext 202 // https://stackoverflow.com/questions/243363/can-someone-explain-the-structure-of-a-pid-in-erlang 203 i := atomic.AddUint64(&c.nextPID, 1) 204 return etf.Pid{ 205 Node: etf.Atom(c.nodename), 206 ID: i, 207 Creation: c.creation, 208 } 209 210 } 211 212 // MakeRef returns atomic reference etf.Ref within this node 213 func (c *core) MakeRef() (ref etf.Ref) { 214 ref.Node = etf.Atom(c.nodename) 215 ref.Creation = c.creation 216 nt := atomic.AddUint64(&c.uniqID, 1) 217 ref.ID[0] = uint32(uint64(nt) & ((2 << 17) - 1)) 218 ref.ID[1] = uint32(uint64(nt) >> 46) 219 return 220 } 221 222 // IsAlias 223 func (c *core) IsAlias(alias etf.Alias) bool { 224 c.mutexAliases.RLock() 225 _, ok := c.aliases[alias] 226 c.mutexAliases.RUnlock() 227 return ok 228 } 229 230 func (c *core) newAlias(p *process) (etf.Alias, error) { 231 var alias etf.Alias 232 233 // chech if its alive 234 c.mutexProcesses.RLock() 235 _, exist := c.processes[p.self.ID] 236 c.mutexProcesses.RUnlock() 237 if !exist { 238 return alias, lib.ErrProcessUnknown 239 } 240 241 alias = etf.Alias(c.MakeRef()) 242 lib.Log("[%s] CORE create process alias for %v: %s", c.nodename, p.self, alias) 243 244 c.mutexAliases.Lock() 245 c.aliases[alias] = p 246 c.mutexAliases.Unlock() 247 248 p.Lock() 249 p.aliases = append(p.aliases, alias) 250 p.Unlock() 251 return alias, nil 252 } 253 254 func (c *core) deleteAlias(owner *process, alias etf.Alias) error { 255 lib.Log("[%s] CORE delete process alias %v for %v", c.nodename, alias, owner.self) 256 257 c.mutexAliases.Lock() 258 p, alias_exist := c.aliases[alias] 259 c.mutexAliases.Unlock() 260 261 if alias_exist == false { 262 return lib.ErrAliasUnknown 263 } 264 265 c.mutexProcesses.RLock() 266 _, process_exist := c.processes[owner.self.ID] 267 c.mutexProcesses.RUnlock() 268 269 if process_exist == false { 270 return lib.ErrProcessUnknown 271 } 272 if p.self != owner.self { 273 return lib.ErrAliasOwner 274 } 275 276 p.Lock() 277 for i := range p.aliases { 278 if alias != p.aliases[i] { 279 continue 280 } 281 // remove it from the global alias list 282 c.mutexAliases.Lock() 283 delete(c.aliases, alias) 284 c.mutexAliases.Unlock() 285 // remove it from the process alias list 286 p.aliases[i] = p.aliases[0] 287 p.aliases = p.aliases[1:] 288 p.Unlock() 289 return nil 290 } 291 p.Unlock() 292 293 // shouldn't reach this code. seems we got a bug 294 c.mutexAliases.Lock() 295 delete(c.aliases, alias) 296 c.mutexAliases.Unlock() 297 lib.Warning("Bug: Process lost its alias. Please, report this issue") 298 299 return lib.ErrAliasUnknown 300 } 301 302 func (c *core) newProcess(name string, behavior gen.ProcessBehavior, opts processOptions) (*process, error) { 303 304 var processContext context.Context 305 var kill context.CancelFunc 306 mailboxSize := DefaultProcessMailboxSize 307 if opts.MailboxSize > 0 { 308 mailboxSize = int(opts.MailboxSize) 309 } 310 directboxSize := DefaultProcessDirectboxSize 311 if opts.DirectboxSize > 0 { 312 directboxSize = int(opts.DirectboxSize) 313 } 314 315 if opts.Context != nil { 316 if opts.Context.Value(c) != c { 317 return nil, lib.ErrProcessContext 318 } 319 processContext, kill = context.WithCancel(opts.Context) 320 } else { 321 processContext, kill = context.WithCancel(c.ctx) 322 } 323 324 pid := c.newPID() 325 326 env := make(map[gen.EnvKey]interface{}) 327 // inherite the node environment 328 c.mutexEnv.RLock() 329 for k, v := range c.env { 330 env[k] = v 331 } 332 c.mutexEnv.RUnlock() 333 334 // merge the custom ones 335 for k, v := range opts.Env { 336 env[k] = v 337 } 338 339 process := &process{ 340 coreInternal: c, 341 342 self: pid, 343 name: name, 344 behavior: behavior, 345 env: env, 346 compression: c.compression, 347 348 parent: opts.parent, 349 groupLeader: opts.GroupLeader, 350 351 mailBox: make(chan gen.ProcessMailboxMessage, mailboxSize), 352 gracefulExit: make(chan gen.ProcessGracefulExitRequest, mailboxSize), 353 direct: make(chan gen.ProcessDirectMessage, directboxSize), 354 355 context: processContext, 356 kill: kill, 357 358 reply: make(map[etf.Ref]chan syncReplyMessage), 359 fallback: opts.Fallback, 360 } 361 362 process.exit = func(from etf.Pid, reason string) error { 363 lib.Log("[%s] EXIT from %s to %s with reason: %s", c.nodename, from, pid, reason) 364 if processContext.Err() != nil { 365 // process is already died 366 return lib.ErrProcessUnknown 367 } 368 369 ex := gen.ProcessGracefulExitRequest{ 370 From: from, 371 Reason: reason, 372 } 373 374 // use select just in case if this process isn't been started yet 375 // or ProcessLoop is already exited (has been set to nil) 376 // otherwise it cause infinity lock 377 select { 378 case process.gracefulExit <- ex: 379 default: 380 return lib.ErrProcessBusy 381 } 382 383 // let the process decide whether to stop itself, otherwise its going to be killed 384 if process.trapExit == false { 385 process.kill() 386 } 387 return nil 388 } 389 390 if name != "" { 391 lib.Log("[%s] CORE registering name (%s): %s", c.nodename, pid, name) 392 c.mutexNames.Lock() 393 if _, exist := c.names[name]; exist { 394 c.mutexNames.Unlock() 395 process.kill() // cancel context 396 return nil, lib.ErrTaken 397 } 398 c.names[name] = process.self 399 c.mutexNames.Unlock() 400 } 401 402 lib.Log("[%s] CORE registering process: %s", c.nodename, pid) 403 c.mutexProcesses.Lock() 404 c.processes[process.self.ID] = process 405 c.mutexProcesses.Unlock() 406 407 return process, nil 408 } 409 410 func (c *core) deleteProcess(pid etf.Pid) { 411 c.mutexProcesses.Lock() 412 p, exist := c.processes[pid.ID] 413 if !exist { 414 c.mutexProcesses.Unlock() 415 return 416 } 417 lib.Log("[%s] CORE unregistering process: %s", c.nodename, p.self) 418 delete(c.processes, pid.ID) 419 c.mutexProcesses.Unlock() 420 421 c.mutexNames.Lock() 422 if (p.name) != "" { 423 lib.Log("[%s] CORE unregistering name (%s): %s", c.nodename, p.self, p.name) 424 delete(c.names, p.name) 425 } 426 427 // delete names registered with this pid 428 for name, pid := range c.names { 429 if p.self == pid { 430 delete(c.names, name) 431 } 432 } 433 c.mutexNames.Unlock() 434 435 c.mutexAliases.Lock() 436 for _, alias := range p.aliases { 437 delete(c.aliases, alias) 438 } 439 c.mutexAliases.Unlock() 440 441 return 442 } 443 444 func (c *core) spawn(name string, opts processOptions, behavior gen.ProcessBehavior, args ...etf.Term) (gen.Process, error) { 445 446 process, err := c.newProcess(name, behavior, opts) 447 if err != nil { 448 return nil, err 449 } 450 lib.Log("[%s] CORE spawn a new process %s (registered name: %q)", c.nodename, process.self, name) 451 452 initProcess := func() (ps gen.ProcessState, err error) { 453 if lib.CatchPanic() { 454 defer func() { 455 if rcv := recover(); rcv != nil { 456 pc, fn, line, _ := runtime.Caller(2) 457 lib.Warning("initialization process failed %s[%q] %#v at %s[%s:%d]", 458 process.self, name, rcv, runtime.FuncForPC(pc).Name(), fn, line) 459 c.deleteProcess(process.self) 460 process.kill() 461 err = fmt.Errorf("panic") 462 } 463 }() 464 } 465 466 ps, err = behavior.ProcessInit(process, args...) 467 return 468 } 469 470 processState, err := initProcess() 471 if err != nil { 472 return nil, err 473 } 474 475 started := make(chan bool) 476 defer close(started) 477 478 cleanProcess := func(reason string) { 479 // set gracefulExit to nil before we start termination handling 480 process.gracefulExit = nil 481 c.deleteProcess(process.self) 482 // invoke cancel context to prevent memory leaks 483 // and propagate context canelation 484 process.kill() 485 // notify all the linked process and monitors 486 c.handleTerminated(process.self, name, reason) 487 // make the rest empty 488 process.Lock() 489 process.aliases = []etf.Alias{} 490 491 // Do not clean self and name. Sometimes its good to know what pid 492 // (and what name) was used by the dead process. (gen.Applications is using it) 493 // process.name = "" 494 // process.self = etf.Pid{} 495 496 process.behavior = nil 497 process.parent = nil 498 process.groupLeader = nil 499 process.exit = nil 500 process.kill = nil 501 process.mailBox = nil 502 process.direct = nil 503 process.env = nil 504 process.reply = nil 505 process.Unlock() 506 } 507 508 go func(ps gen.ProcessState) { 509 if lib.CatchPanic() { 510 defer func() { 511 if rcv := recover(); rcv != nil { 512 pc, fn, line, _ := runtime.Caller(2) 513 lib.Warning("process terminated %s[%q] %#v at %s[%s:%d]", 514 process.self, name, rcv, runtime.FuncForPC(pc).Name(), fn, line) 515 cleanProcess("panic") 516 } 517 }() 518 } 519 520 // start process loop 521 reason := behavior.ProcessLoop(ps, started) 522 // process stopped 523 cleanProcess(reason) 524 525 }(processState) 526 527 // wait for the starting process loop 528 <-started 529 return process, nil 530 } 531 532 func (c *core) registerName(name string, pid etf.Pid) error { 533 lib.Log("[%s] CORE registering name %s", c.nodename, name) 534 c.mutexNames.Lock() 535 defer c.mutexNames.Unlock() 536 if _, ok := c.names[name]; ok { 537 // already registered 538 return lib.ErrTaken 539 } 540 c.names[name] = pid 541 return nil 542 } 543 544 func (c *core) unregisterName(name string) error { 545 lib.Log("[%s] CORE unregistering name %s", c.nodename, name) 546 c.mutexNames.Lock() 547 defer c.mutexNames.Unlock() 548 if _, ok := c.names[name]; ok { 549 delete(c.names, name) 550 return nil 551 } 552 return lib.ErrNameUnknown 553 } 554 555 // ListEnv 556 func (c *core) ListEnv() map[gen.EnvKey]interface{} { 557 c.mutexEnv.RLock() 558 defer c.mutexEnv.RUnlock() 559 560 env := make(map[gen.EnvKey]interface{}) 561 for key, value := range c.env { 562 env[key] = value 563 } 564 565 return env 566 } 567 568 // SetEnv 569 func (c *core) SetEnv(name gen.EnvKey, value interface{}) { 570 c.mutexEnv.Lock() 571 defer c.mutexEnv.Unlock() 572 if strings.HasPrefix(string(name), "ergo:") { 573 return 574 } 575 c.env[name] = value 576 } 577 578 // Env 579 func (c *core) Env(name gen.EnvKey) interface{} { 580 c.mutexEnv.RLock() 581 defer c.mutexEnv.RUnlock() 582 if value, ok := c.env[name]; ok { 583 return value 584 } 585 return nil 586 } 587 588 // RegisterBehavior 589 func (c *core) RegisterBehavior(group, name string, behavior gen.ProcessBehavior, data interface{}) error { 590 lib.Log("[%s] CORE registering behavior %q in group %q ", c.nodename, name, group) 591 var groupBehaviors map[string]gen.RegisteredBehavior 592 var exist bool 593 594 c.mutexBehaviors.Lock() 595 defer c.mutexBehaviors.Unlock() 596 597 groupBehaviors, exist = c.behaviors[group] 598 if !exist { 599 groupBehaviors = make(map[string]gen.RegisteredBehavior) 600 c.behaviors[group] = groupBehaviors 601 } 602 603 _, exist = groupBehaviors[name] 604 if exist { 605 return lib.ErrTaken 606 } 607 608 rb := gen.RegisteredBehavior{ 609 Behavior: behavior, 610 Data: data, 611 } 612 groupBehaviors[name] = rb 613 return nil 614 } 615 616 // RegisteredBehavior 617 func (c *core) RegisteredBehavior(group, name string) (gen.RegisteredBehavior, error) { 618 var groupBehaviors map[string]gen.RegisteredBehavior 619 var rb gen.RegisteredBehavior 620 var exist bool 621 622 c.mutexBehaviors.Lock() 623 defer c.mutexBehaviors.Unlock() 624 625 groupBehaviors, exist = c.behaviors[group] 626 if !exist { 627 return rb, lib.ErrBehaviorGroupUnknown 628 } 629 630 rb, exist = groupBehaviors[name] 631 if !exist { 632 return rb, lib.ErrBehaviorUnknown 633 } 634 return rb, nil 635 } 636 637 // RegisteredBehaviorGroup 638 func (c *core) RegisteredBehaviorGroup(group string) []gen.RegisteredBehavior { 639 var groupBehaviors map[string]gen.RegisteredBehavior 640 var exist bool 641 var listrb []gen.RegisteredBehavior 642 643 c.mutexBehaviors.Lock() 644 defer c.mutexBehaviors.Unlock() 645 646 groupBehaviors, exist = c.behaviors[group] 647 if !exist { 648 return listrb 649 } 650 651 for _, v := range groupBehaviors { 652 listrb = append(listrb, v) 653 } 654 return listrb 655 } 656 657 // UnregisterBehavior 658 func (c *core) UnregisterBehavior(group, name string) error { 659 lib.Log("[%s] CORE unregistering behavior %s in group %s ", c.nodename, name, group) 660 var groupBehaviors map[string]gen.RegisteredBehavior 661 var exist bool 662 663 c.mutexBehaviors.Lock() 664 defer c.mutexBehaviors.Unlock() 665 666 groupBehaviors, exist = c.behaviors[group] 667 if !exist { 668 return lib.ErrBehaviorUnknown 669 } 670 delete(groupBehaviors, name) 671 672 // remove group if its empty 673 if len(groupBehaviors) == 0 { 674 delete(c.behaviors, group) 675 } 676 return nil 677 } 678 679 // ProcessInfo 680 func (c *core) ProcessInfo(pid etf.Pid) (gen.ProcessInfo, error) { 681 p := c.processByPid(pid) 682 if p == nil { 683 return gen.ProcessInfo{}, fmt.Errorf("undefined") 684 } 685 686 return p.Info(), nil 687 } 688 689 // ProcessByPid 690 func (c *core) ProcessByPid(pid etf.Pid) gen.Process { 691 p := c.processByPid(pid) 692 if p == nil { 693 return nil 694 } 695 return p 696 } 697 698 // ProcessByAlias 699 func (c *core) ProcessByAlias(alias etf.Alias) gen.Process { 700 c.mutexAliases.RLock() 701 defer c.mutexAliases.RUnlock() 702 if p, ok := c.aliases[alias]; ok && p.IsAlive() { 703 return p 704 } 705 // unknown process 706 return nil 707 } 708 709 // ProcessByName 710 func (c *core) ProcessByName(name string) gen.Process { 711 var pid etf.Pid 712 if name != "" { 713 // requesting Process by name 714 c.mutexNames.RLock() 715 716 if p, ok := c.names[name]; ok { 717 pid = p 718 } else { 719 c.mutexNames.RUnlock() 720 return nil 721 } 722 c.mutexNames.RUnlock() 723 } 724 725 return c.ProcessByPid(pid) 726 } 727 728 // ProcessList 729 func (c *core) ProcessList() []gen.Process { 730 list := []gen.Process{} 731 c.mutexProcesses.RLock() 732 for _, p := range c.processes { 733 list = append(list, p) 734 } 735 c.mutexProcesses.RUnlock() 736 return list 737 } 738 739 // 740 // implementation of CoreRouter interface: 741 // RouteSend 742 // RouteSendReg 743 // RouteSendAlias 744 // 745 746 // RouteSend implements RouteSend method of Router interface 747 func (c *core) RouteSend(from etf.Pid, to etf.Pid, message etf.Term) error { 748 if string(to.Node) == c.nodename { 749 if to.Creation != c.creation { 750 // message is addressed to the previous incarnation of this PID 751 lib.Warning("message from %s is addressed to the previous incarnation of this PID %s", from, to) 752 return lib.ErrProcessIncarnation 753 } 754 // local route 755 c.mutexProcesses.RLock() 756 p, exist := c.processes[to.ID] 757 c.mutexProcesses.RUnlock() 758 if !exist { 759 lib.Log("[%s] CORE route message by pid (local) %s failed. Unknown process", c.nodename, to) 760 return lib.ErrProcessUnknown 761 } 762 lib.Log("[%s] CORE route message by pid (local) %s", c.nodename, to) 763 select { 764 case p.mailBox <- gen.ProcessMailboxMessage{From: from, Message: message}: 765 default: 766 c.mutexNames.RLock() 767 pid, found := c.names[p.fallback.Name] 768 c.mutexNames.RUnlock() 769 if found == false { 770 lib.Warning("mailbox of %s[%q] is full. dropped message from %s", p.self, p.name, from) 771 return lib.ErrProcessMailboxFull 772 } 773 fbm := gen.MessageFallback{ 774 Process: p.self, 775 Tag: p.fallback.Tag, 776 Message: message, 777 } 778 return c.RouteSend(from, pid, fbm) 779 } 780 return nil 781 } 782 783 // do not allow to send from the alien node. 784 if string(from.Node) != c.nodename { 785 return lib.ErrSenderUnknown 786 } 787 788 // sending to remote node 789 c.mutexProcesses.RLock() 790 p_from, exist := c.processes[from.ID] 791 c.mutexProcesses.RUnlock() 792 if !exist { 793 lib.Log("[%s] CORE route message by pid (remote) %s failed. Unknown sender", c.nodename, to) 794 return lib.ErrSenderUnknown 795 } 796 connection, err := c.getConnection(string(to.Node)) 797 if err != nil { 798 return err 799 } 800 801 lib.Log("[%s] CORE route message by pid (remote) %s", c.nodename, to) 802 return connection.Send(p_from, to, message) 803 } 804 805 // RouteSendReg implements RouteSendReg method of Router interface 806 func (c *core) RouteSendReg(from etf.Pid, to gen.ProcessID, message etf.Term) error { 807 if to.Node == c.nodename { 808 // local route 809 c.mutexNames.RLock() 810 pid, ok := c.names[to.Name] 811 c.mutexNames.RUnlock() 812 if !ok { 813 lib.Log("[%s] CORE route message by gen.ProcessID (local) %s failed. Unknown process", c.nodename, to) 814 return lib.ErrProcessUnknown 815 } 816 lib.Log("[%s] CORE route message by gen.ProcessID (local) %s", c.nodename, to) 817 return c.RouteSend(from, pid, message) 818 } 819 820 // do not allow to send from the alien node. 821 if string(from.Node) != c.nodename { 822 return lib.ErrSenderUnknown 823 } 824 825 // send to remote node 826 c.mutexProcesses.RLock() 827 p_from, exist := c.processes[from.ID] 828 c.mutexProcesses.RUnlock() 829 if !exist { 830 lib.Log("[%s] CORE route message by gen.ProcessID (remote) %s failed. Unknown sender", c.nodename, to) 831 return lib.ErrSenderUnknown 832 } 833 connection, err := c.getConnection(string(to.Node)) 834 if err != nil { 835 return err 836 } 837 838 lib.Log("[%s] CORE route message by gen.ProcessID (remote) %s", c.nodename, to) 839 return connection.SendReg(p_from, to, message) 840 } 841 842 // RouteSendAlias implements RouteSendAlias method of Router interface 843 func (c *core) RouteSendAlias(from etf.Pid, to etf.Alias, message etf.Term) error { 844 845 if string(to.Node) == c.nodename { 846 // local route by alias 847 c.mutexAliases.RLock() 848 process, ok := c.aliases[to] 849 c.mutexAliases.RUnlock() 850 if !ok { 851 lib.Log("[%s] CORE route message by alias (local) %s failed. Unknown process", c.nodename, to) 852 return lib.ErrProcessUnknown 853 } 854 lib.Log("[%s] CORE route message by alias (local) %s", c.nodename, to) 855 return c.RouteSend(from, process.self, message) 856 } 857 858 // do not allow to send from the alien node. Proxy request must be used. 859 if string(from.Node) != c.nodename { 860 return lib.ErrSenderUnknown 861 } 862 863 // send to remote node 864 c.mutexProcesses.RLock() 865 p_from, exist := c.processes[from.ID] 866 c.mutexProcesses.RUnlock() 867 if !exist { 868 lib.Log("[%s] CORE route message by alias (remote) %s failed. Unknown sender", c.nodename, to) 869 return lib.ErrSenderUnknown 870 } 871 connection, err := c.getConnection(string(to.Node)) 872 if err != nil { 873 return err 874 } 875 876 lib.Log("[%s] CORE route message by alias (remote) %s", c.nodename, to) 877 return connection.SendAlias(p_from, to, message) 878 } 879 880 // RouteSpawnRequest 881 func (c *core) RouteSpawnRequest(node string, behaviorName string, request gen.RemoteSpawnRequest, args ...etf.Term) error { 882 if node == c.nodename { 883 // get connection for reply 884 connection, err := c.getConnection(string(request.From.Node)) 885 if err != nil { 886 return err 887 } 888 889 // check if we have registered behavior with given name 890 b, err := c.RegisteredBehavior(remoteBehaviorGroup, behaviorName) 891 if err != nil { 892 return connection.SpawnReplyError(request.From, request.Ref, err) 893 } 894 895 // spawn new process 896 process_opts := processOptions{} 897 process_opts.Env = map[gen.EnvKey]interface{}{EnvKeyRemoteSpawn: request.Options} 898 process, err_spawn := c.spawn(request.Options.Name, process_opts, b.Behavior, args...) 899 900 // reply 901 if err_spawn != nil { 902 return connection.SpawnReplyError(request.From, request.Ref, err_spawn) 903 } 904 return connection.SpawnReply(request.From, request.Ref, process.Self()) 905 } 906 907 connection, err := c.getConnection(node) 908 if err != nil { 909 return err 910 } 911 return connection.SpawnRequest(node, behaviorName, request, args...) 912 } 913 914 // RouteSpawnReply 915 func (c *core) RouteSpawnReply(to etf.Pid, ref etf.Ref, result etf.Term) error { 916 process := c.processByPid(to) 917 if process == nil { 918 // seems process terminated 919 return lib.ErrProcessTerminated 920 } 921 process.PutSyncReply(ref, result, nil) 922 return nil 923 } 924 925 func (c *core) processByPid(pid etf.Pid) *process { 926 c.mutexProcesses.RLock() 927 defer c.mutexProcesses.RUnlock() 928 if p, ok := c.processes[pid.ID]; ok && p.IsAlive() { 929 return p 930 } 931 // unknown process 932 return nil 933 } 934 935 func (c *core) coreStats() internalCoreStats { 936 stats := internalCoreStats{} 937 stats.totalProcesses = atomic.LoadUint64(&c.nextPID) - startPID 938 stats.totalReferences = atomic.LoadUint64(&c.uniqID) - startUniqID 939 940 c.mutexProcesses.RLock() 941 stats.processes = len(c.processes) 942 c.mutexProcesses.RUnlock() 943 944 c.mutexAliases.RLock() 945 stats.aliases = len(c.aliases) 946 c.mutexAliases.RUnlock() 947 948 c.mutexNames.RLock() 949 stats.names = len(c.names) 950 c.mutexNames.RUnlock() 951 return stats 952 }