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  }