github.com/ergo-services/ergo@v1.999.224/node/process.go (about)

     1  package node
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"time"
     8  
     9  	"github.com/ergo-services/ergo/etf"
    10  	"github.com/ergo-services/ergo/gen"
    11  	"github.com/ergo-services/ergo/lib"
    12  )
    13  
    14  var (
    15  	syncReplyChannels = &sync.Pool{
    16  		New: func() interface{} {
    17  			return make(chan syncReplyMessage, 2)
    18  		},
    19  	}
    20  )
    21  
    22  type syncReplyMessage struct {
    23  	value etf.Term
    24  	err   error
    25  }
    26  
    27  type process struct {
    28  	coreInternal
    29  	sync.RWMutex
    30  
    31  	name     string
    32  	self     etf.Pid
    33  	behavior gen.ProcessBehavior
    34  	env      map[gen.EnvKey]interface{}
    35  
    36  	parent      *process
    37  	groupLeader gen.Process
    38  	aliases     []etf.Alias
    39  
    40  	mailBox      chan gen.ProcessMailboxMessage
    41  	gracefulExit chan gen.ProcessGracefulExitRequest
    42  	direct       chan gen.ProcessDirectMessage
    43  
    44  	context context.Context
    45  	kill    context.CancelFunc
    46  	exit    processExitFunc
    47  
    48  	replyMutex sync.RWMutex
    49  	reply      map[etf.Ref]chan syncReplyMessage
    50  
    51  	trapExit    bool
    52  	compression Compression
    53  
    54  	fallback gen.ProcessFallback
    55  }
    56  
    57  type processOptions struct {
    58  	gen.ProcessOptions
    59  	parent *process
    60  }
    61  
    62  type processExitFunc func(from etf.Pid, reason string) error
    63  
    64  // Self
    65  func (p *process) Self() etf.Pid {
    66  	return p.self
    67  }
    68  
    69  // Name
    70  func (p *process) Name() string {
    71  	return p.name
    72  }
    73  
    74  // RegisterName
    75  func (p *process) RegisterName(name string) error {
    76  	if p.behavior == nil {
    77  		return lib.ErrProcessTerminated
    78  	}
    79  	return p.registerName(name, p.self)
    80  }
    81  
    82  // UnregisterName
    83  func (p *process) UnregisterName(name string) error {
    84  	if p.behavior == nil {
    85  		return lib.ErrProcessTerminated
    86  	}
    87  	prc := p.ProcessByName(name)
    88  	if prc == nil {
    89  		return lib.ErrNameUnknown
    90  	}
    91  	if prc.Self() != p.self {
    92  		return lib.ErrNameOwner
    93  	}
    94  	return p.unregisterName(name)
    95  }
    96  
    97  // Kill
    98  func (p *process) Kill() {
    99  	if p.behavior == nil {
   100  		return
   101  	}
   102  	p.kill()
   103  }
   104  
   105  // Exit
   106  func (p *process) Exit(reason string) error {
   107  	if p.behavior == nil {
   108  		return lib.ErrProcessTerminated
   109  	}
   110  	return p.exit(p.self, reason)
   111  }
   112  
   113  // Context
   114  func (p *process) Context() context.Context {
   115  	return p.context
   116  }
   117  
   118  // Parent
   119  func (p *process) Parent() gen.Process {
   120  	if p.parent == nil {
   121  		return nil
   122  	}
   123  	return p.parent
   124  }
   125  
   126  // GroupLeader
   127  func (p *process) GroupLeader() gen.Process {
   128  	if p.groupLeader == nil {
   129  		return nil
   130  	}
   131  	return p.groupLeader
   132  }
   133  
   134  // Links
   135  func (p *process) Links() []etf.Pid {
   136  	return p.processLinks(p.self)
   137  }
   138  
   139  // Monitors
   140  func (p *process) Monitors() []etf.Pid {
   141  	return p.processMonitors(p.self)
   142  }
   143  
   144  // MonitorsByName
   145  func (p *process) MonitorsByName() []gen.ProcessID {
   146  	return p.processMonitorsByName(p.self)
   147  }
   148  
   149  // MonitoredBy
   150  func (p *process) MonitoredBy() []etf.Pid {
   151  	return p.processMonitoredBy(p.self)
   152  }
   153  
   154  // Aliases
   155  func (p *process) Aliases() []etf.Alias {
   156  	return p.aliases
   157  }
   158  
   159  // Info
   160  func (p *process) Info() gen.ProcessInfo {
   161  	p.RLock()
   162  	if p.behavior == nil {
   163  		p.RUnlock()
   164  		return gen.ProcessInfo{}
   165  	}
   166  
   167  	gl := p.self
   168  	if p.groupLeader != nil {
   169  		gl = p.groupLeader.Self()
   170  	}
   171  	p.RUnlock()
   172  
   173  	links := p.Links()
   174  	monitors := p.Monitors()
   175  	monitorsByName := p.MonitorsByName()
   176  	monitoredBy := p.MonitoredBy()
   177  	return gen.ProcessInfo{
   178  		PID:             p.self,
   179  		Name:            p.name,
   180  		GroupLeader:     gl,
   181  		Links:           links,
   182  		Monitors:        monitors,
   183  		MonitorsByName:  monitorsByName,
   184  		MonitoredBy:     monitoredBy,
   185  		Aliases:         p.aliases,
   186  		Status:          "running",
   187  		MessageQueueLen: len(p.mailBox),
   188  		TrapExit:        p.trapExit,
   189  		Compression:     p.compression.Enable,
   190  	}
   191  }
   192  
   193  // Send
   194  func (p *process) Send(to interface{}, message etf.Term) error {
   195  	p.RLock()
   196  	if p.behavior == nil {
   197  		p.RUnlock()
   198  		return lib.ErrProcessTerminated
   199  	}
   200  	p.RUnlock()
   201  
   202  	switch receiver := to.(type) {
   203  	case etf.Pid:
   204  		return p.RouteSend(p.self, receiver, message)
   205  	case string:
   206  		return p.RouteSendReg(p.self, gen.ProcessID{Name: receiver, Node: string(p.self.Node)}, message)
   207  	case etf.Atom:
   208  		return p.RouteSendReg(p.self, gen.ProcessID{Name: string(receiver), Node: string(p.self.Node)}, message)
   209  	case gen.ProcessID:
   210  		return p.RouteSendReg(p.self, receiver, message)
   211  	case etf.Alias:
   212  		return p.RouteSendAlias(p.self, receiver, message)
   213  	}
   214  	return fmt.Errorf("Unknown receiver type")
   215  }
   216  
   217  // SendAfter
   218  func (p *process) SendAfter(to interface{}, message etf.Term, after time.Duration) gen.CancelFunc {
   219  
   220  	timer := time.AfterFunc(after, func() { p.Send(to, message) })
   221  	return timer.Stop
   222  }
   223  
   224  // CreateAlias
   225  func (p *process) CreateAlias() (etf.Alias, error) {
   226  	p.RLock()
   227  	if p.behavior == nil {
   228  		p.RUnlock()
   229  		return etf.Alias{}, lib.ErrProcessTerminated
   230  	}
   231  	p.RUnlock()
   232  	return p.newAlias(p)
   233  }
   234  
   235  // DeleteAlias
   236  func (p *process) DeleteAlias(alias etf.Alias) error {
   237  	p.RLock()
   238  	if p.behavior == nil {
   239  		p.RUnlock()
   240  		return lib.ErrProcessTerminated
   241  	}
   242  	p.RUnlock()
   243  	return p.deleteAlias(p, alias)
   244  }
   245  
   246  // ListEnv
   247  func (p *process) ListEnv() map[gen.EnvKey]interface{} {
   248  	p.RLock()
   249  	defer p.RUnlock()
   250  
   251  	env := make(map[gen.EnvKey]interface{})
   252  
   253  	if p.groupLeader != nil {
   254  		for key, value := range p.groupLeader.ListEnv() {
   255  			env[key] = value
   256  		}
   257  	}
   258  	if p.parent != nil {
   259  		for key, value := range p.parent.ListEnv() {
   260  			env[key] = value
   261  		}
   262  	}
   263  	for key, value := range p.env {
   264  		env[key] = value
   265  	}
   266  
   267  	return env
   268  }
   269  
   270  // SetEnv
   271  func (p *process) SetEnv(name gen.EnvKey, value interface{}) {
   272  	p.Lock()
   273  	defer p.Unlock()
   274  
   275  	if value == nil {
   276  		delete(p.env, name)
   277  		return
   278  	}
   279  	p.env[name] = value
   280  }
   281  
   282  // Env
   283  func (p *process) Env(name gen.EnvKey) interface{} {
   284  	p.RLock()
   285  	defer p.RUnlock()
   286  
   287  	if value, ok := p.env[name]; ok {
   288  		return value
   289  	}
   290  
   291  	if p.groupLeader != nil {
   292  		return p.groupLeader.Env(name)
   293  	}
   294  
   295  	return nil
   296  }
   297  
   298  // Wait
   299  func (p *process) Wait() {
   300  	if p.IsAlive() {
   301  		<-p.context.Done()
   302  	}
   303  }
   304  
   305  // WaitWithTimeout
   306  func (p *process) WaitWithTimeout(d time.Duration) error {
   307  	if !p.IsAlive() {
   308  		return nil
   309  	}
   310  
   311  	timer := time.NewTimer(d)
   312  	defer timer.Stop()
   313  
   314  	select {
   315  	case <-timer.C:
   316  		return lib.ErrTimeout
   317  	case <-p.context.Done():
   318  		return nil
   319  	}
   320  }
   321  
   322  // Link
   323  func (p *process) Link(with etf.Pid) error {
   324  	p.RLock()
   325  	if p.behavior == nil {
   326  		p.RUnlock()
   327  		return lib.ErrProcessTerminated
   328  	}
   329  	p.RUnlock()
   330  	return p.RouteLink(p.self, with)
   331  }
   332  
   333  // Unlink
   334  func (p *process) Unlink(with etf.Pid) error {
   335  	p.RLock()
   336  	if p.behavior == nil {
   337  		p.RUnlock()
   338  		return lib.ErrProcessTerminated
   339  	}
   340  	p.RUnlock()
   341  	return p.RouteUnlink(p.self, with)
   342  }
   343  
   344  // IsAlive
   345  func (p *process) IsAlive() bool {
   346  	p.RLock()
   347  	defer p.RUnlock()
   348  	if p.behavior == nil {
   349  		return false
   350  	}
   351  	return p.context.Err() == nil
   352  }
   353  
   354  // NodeName
   355  func (p *process) NodeName() string {
   356  	return p.coreNodeName()
   357  }
   358  
   359  // NodeStop
   360  func (p *process) NodeStop() {
   361  	p.coreStop()
   362  }
   363  
   364  // NodeUptime
   365  func (p *process) NodeUptime() int64 {
   366  	return p.coreUptime()
   367  }
   368  
   369  // Children
   370  func (p *process) Children() ([]etf.Pid, error) {
   371  	c, err := p.Direct(gen.MessageDirectChildren{})
   372  	if err != nil {
   373  		return []etf.Pid{}, err
   374  	}
   375  	children, correct := c.([]etf.Pid)
   376  	if correct == false {
   377  		return []etf.Pid{}, err
   378  	}
   379  	return children, nil
   380  }
   381  
   382  // SetTrapExit
   383  func (p *process) SetTrapExit(trap bool) {
   384  	p.trapExit = trap
   385  }
   386  
   387  // TrapExit
   388  func (p *process) TrapExit() bool {
   389  	return p.trapExit
   390  }
   391  
   392  // SetCompression
   393  func (p *process) SetCompression(enable bool) {
   394  	p.compression.Enable = enable
   395  }
   396  
   397  // Compression
   398  func (p *process) Compression() bool {
   399  	return p.compression.Enable
   400  }
   401  
   402  // CompressionLevel
   403  func (p *process) CompressionLevel() int {
   404  	return p.compression.Level
   405  }
   406  
   407  // SetCompressionLevel
   408  func (p *process) SetCompressionLevel(level int) bool {
   409  	if level < 1 || level > 9 {
   410  		return false
   411  	}
   412  	p.compression.Level = level
   413  	return true
   414  }
   415  
   416  // CompressionThreshold
   417  func (p *process) CompressionThreshold() int {
   418  	return p.compression.Threshold
   419  }
   420  
   421  // SetCompressionThreshold
   422  func (p *process) SetCompressionThreshold(threshold int) bool {
   423  	if threshold < DefaultCompressionThreshold {
   424  		return false
   425  	}
   426  	p.compression.Threshold = threshold
   427  	return true
   428  }
   429  
   430  // Behavior
   431  func (p *process) Behavior() gen.ProcessBehavior {
   432  	p.RLock()
   433  	defer p.RUnlock()
   434  
   435  	if p.behavior == nil {
   436  		return nil
   437  	}
   438  	return p.behavior
   439  }
   440  
   441  // Direct
   442  func (p *process) Direct(request interface{}) (interface{}, error) {
   443  	return p.DirectWithTimeout(request, gen.DefaultCallTimeout)
   444  }
   445  
   446  // DirectWithTimeout
   447  func (p *process) DirectWithTimeout(request interface{}, timeout int) (interface{}, error) {
   448  	if timeout < 1 {
   449  		timeout = gen.DefaultCallTimeout
   450  	}
   451  
   452  	direct := gen.ProcessDirectMessage{
   453  		Ref:     p.MakeRef(),
   454  		Message: request,
   455  	}
   456  
   457  	if err := p.PutSyncRequest(direct.Ref); err != nil {
   458  		return nil, err
   459  	}
   460  
   461  	// sending request
   462  	select {
   463  	case p.direct <- direct:
   464  	default:
   465  		p.CancelSyncRequest(direct.Ref)
   466  		return nil, lib.ErrProcessBusy
   467  	}
   468  
   469  	return p.WaitSyncReply(direct.Ref, timeout)
   470  }
   471  
   472  func (p *process) RegisterEvent(event gen.Event, messages ...gen.EventMessage) error {
   473  	return p.registerEvent(p.self, event, messages)
   474  }
   475  
   476  func (p *process) UnregisterEvent(event gen.Event) error {
   477  	return p.unregisterEvent(p.self, event)
   478  }
   479  
   480  func (p *process) MonitorEvent(event gen.Event) error {
   481  	return p.monitorEvent(p.self, event)
   482  }
   483  
   484  func (p *process) DemonitorEvent(event gen.Event) error {
   485  	return p.demonitorEvent(p.self, event)
   486  }
   487  
   488  func (p *process) SendEventMessage(event gen.Event, message gen.EventMessage) error {
   489  	return p.sendEvent(p.self, event, message)
   490  }
   491  
   492  // MonitorNode
   493  func (p *process) MonitorNode(name string) etf.Ref {
   494  	ref := p.MakeRef()
   495  	p.monitorNode(p.self, name, ref)
   496  	return ref
   497  }
   498  
   499  // DemonitorNode
   500  func (p *process) DemonitorNode(ref etf.Ref) bool {
   501  	return p.demonitorNode(ref)
   502  }
   503  
   504  // MonitorProcess
   505  func (p *process) MonitorProcess(process interface{}) etf.Ref {
   506  	ref := p.MakeRef()
   507  	switch mp := process.(type) {
   508  	case etf.Pid:
   509  		p.RouteMonitor(p.self, mp, ref)
   510  		return ref
   511  	case gen.ProcessID:
   512  		p.RouteMonitorReg(p.self, mp, ref)
   513  		return ref
   514  	case string:
   515  		p.RouteMonitorReg(p.self, gen.ProcessID{Name: mp, Node: string(p.self.Node)}, ref)
   516  		return ref
   517  	case etf.Atom:
   518  		p.RouteMonitorReg(p.self, gen.ProcessID{Name: string(mp), Node: string(p.self.Node)}, ref)
   519  		return ref
   520  	}
   521  
   522  	// create fake gen.ProcessID. Monitor will send MessageDown with "noproc" as a reason
   523  	p.RouteMonitorReg(p.self, gen.ProcessID{Node: string(p.self.Node)}, ref)
   524  	return ref
   525  }
   526  
   527  // DemonitorProcess
   528  func (p *process) DemonitorProcess(ref etf.Ref) bool {
   529  	if err := p.RouteDemonitor(p.self, ref); err != nil {
   530  		return false
   531  	}
   532  	return true
   533  }
   534  
   535  // RemoteSpawn makes request to spawn new process on a remote node
   536  func (p *process) RemoteSpawn(node string, object string, opts gen.RemoteSpawnOptions, args ...etf.Term) (etf.Pid, error) {
   537  	return p.RemoteSpawnWithTimeout(gen.DefaultCallTimeout, node, object, opts, args...)
   538  }
   539  
   540  // RemoteSpawnWithTimeout makes request to spawn new process on a remote node with given timeout
   541  func (p *process) RemoteSpawnWithTimeout(timeout int, node string, object string, opts gen.RemoteSpawnOptions, args ...etf.Term) (etf.Pid, error) {
   542  	ref := p.MakeRef()
   543  	p.PutSyncRequest(ref)
   544  	request := gen.RemoteSpawnRequest{
   545  		From:    p.self,
   546  		Ref:     ref,
   547  		Options: opts,
   548  	}
   549  	if err := p.RouteSpawnRequest(node, object, request, args...); err != nil {
   550  		p.CancelSyncRequest(ref)
   551  		return etf.Pid{}, err
   552  	}
   553  
   554  	reply, err := p.WaitSyncReply(ref, timeout)
   555  	if err != nil {
   556  		return etf.Pid{}, err
   557  	}
   558  
   559  	// Result of the operation. If Result is a process identifier,
   560  	// the operation succeeded and the process identifier is the
   561  	// identifier of the newly created process. If Result is an atom,
   562  	// the operation failed and the atom identifies failure reason.
   563  	switch r := reply.(type) {
   564  	case etf.Pid:
   565  		m := etf.Ref{} // empty reference
   566  		if opts.Monitor != m {
   567  			p.RouteMonitor(p.self, r, opts.Monitor)
   568  		}
   569  		if opts.Link {
   570  			p.RouteLink(p.self, r)
   571  		}
   572  		return r, nil
   573  	case etf.Atom:
   574  		switch string(r) {
   575  		case lib.ErrTaken.Error():
   576  			return etf.Pid{}, lib.ErrTaken
   577  		case lib.ErrBehaviorUnknown.Error():
   578  			return etf.Pid{}, lib.ErrBehaviorUnknown
   579  		}
   580  		return etf.Pid{}, fmt.Errorf(string(r))
   581  	}
   582  
   583  	return etf.Pid{}, fmt.Errorf("unknown result: %#v", reply)
   584  }
   585  
   586  // Spawn
   587  func (p *process) Spawn(name string, opts gen.ProcessOptions, behavior gen.ProcessBehavior, args ...etf.Term) (gen.Process, error) {
   588  	options := processOptions{
   589  		ProcessOptions: opts,
   590  		parent:         p,
   591  	}
   592  	return p.spawn(name, options, behavior, args...)
   593  }
   594  
   595  // PutSyncRequest
   596  func (p *process) PutSyncRequest(ref etf.Ref) error {
   597  	var preply map[etf.Ref]chan syncReplyMessage
   598  	p.RLock()
   599  	preply = p.reply
   600  	p.RUnlock()
   601  
   602  	if preply == nil {
   603  		return lib.ErrProcessTerminated
   604  	}
   605  
   606  	reply := syncReplyChannels.Get().(chan syncReplyMessage)
   607  	p.replyMutex.Lock()
   608  	preply[ref] = reply
   609  	p.replyMutex.Unlock()
   610  	return nil
   611  }
   612  
   613  // PutSyncReply
   614  func (p *process) PutSyncReply(ref etf.Ref, reply etf.Term, err error) error {
   615  	var preply map[etf.Ref]chan syncReplyMessage
   616  	p.RLock()
   617  	preply = p.reply
   618  	p.RUnlock()
   619  
   620  	if preply == nil {
   621  		return lib.ErrProcessTerminated
   622  	}
   623  
   624  	p.replyMutex.RLock()
   625  	rep, ok := preply[ref]
   626  	defer p.replyMutex.RUnlock()
   627  
   628  	if !ok {
   629  		// no process waiting for it
   630  		return lib.ErrReferenceUnknown
   631  	}
   632  	select {
   633  	case rep <- syncReplyMessage{value: reply, err: err}:
   634  	}
   635  	return nil
   636  }
   637  
   638  // CancelSyncRequest
   639  func (p *process) CancelSyncRequest(ref etf.Ref) {
   640  	var preply map[etf.Ref]chan syncReplyMessage
   641  	p.RLock()
   642  	preply = p.reply
   643  	p.RUnlock()
   644  
   645  	if preply == nil {
   646  		return
   647  	}
   648  
   649  	p.replyMutex.Lock()
   650  	delete(preply, ref)
   651  	p.replyMutex.Unlock()
   652  }
   653  
   654  // WaitSyncReply
   655  func (p *process) WaitSyncReply(ref etf.Ref, timeout int) (etf.Term, error) {
   656  	var preply map[etf.Ref]chan syncReplyMessage
   657  	p.RLock()
   658  	preply = p.reply
   659  	p.RUnlock()
   660  
   661  	if preply == nil {
   662  		return nil, lib.ErrProcessTerminated
   663  	}
   664  
   665  	p.replyMutex.RLock()
   666  	reply, wait_for_reply := preply[ref]
   667  	p.replyMutex.RUnlock()
   668  
   669  	if wait_for_reply == false {
   670  		return nil, fmt.Errorf("Unknown request")
   671  	}
   672  
   673  	defer func(ref etf.Ref) {
   674  		p.replyMutex.Lock()
   675  		delete(preply, ref)
   676  		p.replyMutex.Unlock()
   677  	}(ref)
   678  
   679  	timer := lib.TakeTimer()
   680  	defer lib.ReleaseTimer(timer)
   681  	timer.Reset(time.Second * time.Duration(timeout))
   682  
   683  	for {
   684  		select {
   685  		case m := <-reply:
   686  			// get back 'reply' struct to the pool
   687  			syncReplyChannels.Put(reply)
   688  			return m.value, m.err
   689  		case <-timer.C:
   690  			return nil, lib.ErrTimeout
   691  		case <-p.context.Done():
   692  			return nil, lib.ErrProcessTerminated
   693  		}
   694  	}
   695  
   696  }
   697  
   698  // ProcessChannels
   699  func (p *process) ProcessChannels() gen.ProcessChannels {
   700  	return gen.ProcessChannels{
   701  		Mailbox:      p.mailBox,
   702  		Direct:       p.direct,
   703  		GracefulExit: p.gracefulExit,
   704  	}
   705  }