github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/singleton.go (about)

     1  // Copyright 2018-2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License.
     2  
     3  package runner
     4  
     5  // This file contains the implementation of code that checks to ensure
     6  // that the local machine only has one entity accessing a named resource.
     7  // This allows callers of this code to create and test for exclusive
     8  // access to resources, or to check that only one instance of a
     9  // process is running.
    10  
    11  import (
    12  	"net"
    13  
    14  	"github.com/go-stack/stack"
    15  	"github.com/jjeffery/kv" // MIT License
    16  )
    17  
    18  // Exclusive is a data structure used to tracking and ensure only one
    19  // instance of the go runner is active on a system at once
    20  //
    21  type Exclusive struct {
    22  	Name     string
    23  	ReleaseC chan struct{}
    24  	listen   net.Listener
    25  }
    26  
    27  // NewExclusive is used to initialize a unix domain socket that ensure that only one
    28  // runner process is active on a kubernetes pod or machine at the same time.  If there
    29  // are other processes active then it will return an error.
    30  //
    31  func NewExclusive(name string, quitC chan struct{}) (excl *Exclusive, err kv.Error) {
    32  
    33  	excl = &Exclusive{
    34  		Name:     name,
    35  		ReleaseC: quitC,
    36  	}
    37  
    38  	// Construct an abstract name socket that allows the name to be recycled between process
    39  	// restarts without needing to unlink etc. For more information please see
    40  	// https://gavv.github.io/blog/unix-socket-reuse/, and
    41  	// http://man7.org/linux/man-pages/man7/unix.7.html
    42  	sockName := "@/tmp/"
    43  	sockName += name
    44  
    45  	listen, errGo := net.Listen("unix", sockName)
    46  	if errGo != nil {
    47  		return nil, kv.Wrap(errGo).With("stack", stack.Trace().TrimRuntime())
    48  	}
    49  	excl.listen = listen
    50  
    51  	go func() {
    52  		go excl.listen.Accept()
    53  		<-excl.ReleaseC
    54  	}()
    55  	return excl, nil
    56  }