go.dedis.ch/onet/v3@v3.2.11-0.20210930124529-e36530bca7ef/treestorage.go (about)

     1  package onet
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  type treeStorage struct {
     9  	sync.Mutex
    10  	timeout       time.Duration
    11  	wg            sync.WaitGroup
    12  	trees         map[TreeID]*Tree
    13  	cancellations map[TreeID]chan struct{}
    14  	closed        bool
    15  }
    16  
    17  func newTreeStorage(t time.Duration) *treeStorage {
    18  	return &treeStorage{
    19  		timeout:       t,
    20  		trees:         make(map[TreeID]*Tree),
    21  		cancellations: make(map[TreeID]chan struct{}),
    22  		closed:        false,
    23  	}
    24  }
    25  
    26  // Register creates the key for tree so it is known
    27  func (ts *treeStorage) Register(id TreeID) {
    28  	ts.Lock()
    29  	ts.trees[id] = nil
    30  	ts.Unlock()
    31  }
    32  
    33  // Unregister makes sure the tree is either set or the key is removed
    34  func (ts *treeStorage) Unregister(id TreeID) {
    35  	ts.Lock()
    36  	defer ts.Unlock()
    37  
    38  	if tree := ts.trees[id]; tree == nil {
    39  		// if another goroutine set the tree inbetween, we keep the tree
    40  		// but if it is nil, we need to remove the key
    41  		delete(ts.trees, id)
    42  	}
    43  }
    44  
    45  // IsRegistered returns true when the tree has previously been registered
    46  func (ts *treeStorage) IsRegistered(id TreeID) bool {
    47  	ts.Lock()
    48  	defer ts.Unlock()
    49  
    50  	_, ok := ts.trees[id]
    51  	return ok
    52  }
    53  
    54  // Get returns the tree if it exists or nil
    55  func (ts *treeStorage) Get(id TreeID) *Tree {
    56  	ts.Lock()
    57  	defer ts.Unlock()
    58  
    59  	return ts.trees[id]
    60  }
    61  
    62  // getAndRefresh cancels any pending remove and return the
    63  // tree if it exists.
    64  func (ts *treeStorage) getAndRefresh(id TreeID) *Tree {
    65  	ts.Lock()
    66  	defer ts.Unlock()
    67  
    68  	ts.cancelDeletion(id)
    69  
    70  	return ts.trees[id]
    71  }
    72  
    73  // Set sets the given tree and cancel potential removal
    74  func (ts *treeStorage) Set(tree *Tree) {
    75  	ts.Lock()
    76  	defer ts.Unlock()
    77  
    78  	ts.cancelDeletion(tree.ID)
    79  
    80  	ts.trees[tree.ID] = tree
    81  }
    82  
    83  // Remove starts a timeout to remove the tree from the storage
    84  func (ts *treeStorage) Remove(id TreeID) {
    85  	ts.Lock()
    86  	defer ts.Unlock()
    87  
    88  	if ts.closed {
    89  		// server is closing so we avoid creating new goroutines
    90  		return
    91  	}
    92  
    93  	_, ok := ts.cancellations[id]
    94  	if ok {
    95  		// already planned to be removed
    96  		return
    97  	}
    98  
    99  	ts.wg.Add(1)
   100  	c := make(chan struct{})
   101  	ts.cancellations[id] = c
   102  
   103  	go func() {
   104  		defer ts.wg.Done()
   105  
   106  		timer := time.NewTimer(ts.timeout)
   107  
   108  		select {
   109  		// other distant node instances of the protocol could ask for the tree even
   110  		// after we're done locally and then it needs to be kept around for some time
   111  		case <-timer.C:
   112  			ts.Lock()
   113  			delete(ts.trees, id)
   114  			delete(ts.cancellations, id)
   115  			ts.Unlock()
   116  		case <-c:
   117  			timer.Stop()
   118  			return
   119  		}
   120  	}()
   121  }
   122  
   123  // GetRoster looks for the roster in the list of trees or returns nil
   124  func (ts *treeStorage) GetRoster(id RosterID) *Roster {
   125  	ts.Lock()
   126  	defer ts.Unlock()
   127  
   128  	for _, tree := range ts.trees {
   129  		if tree.Roster.ID.Equal(id) {
   130  			return tree.Roster
   131  		}
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  // Close forces cleaning goroutines to be shutdown
   138  func (ts *treeStorage) Close() {
   139  	ts.Lock()
   140  	defer ts.Unlock()
   141  
   142  	// prevent further call to remove because the server is closing anyway
   143  	ts.closed = true
   144  
   145  	for k, c := range ts.cancellations {
   146  		close(c)
   147  		delete(ts.cancellations, k)
   148  	}
   149  
   150  	ts.wg.Wait()
   151  }
   152  
   153  // cancelDeletion prevents any pending remove request
   154  // to be triggered for the tree.
   155  // Caution: caller is reponsible for holding the lock.
   156  func (ts *treeStorage) cancelDeletion(id TreeID) {
   157  	c := ts.cancellations[id]
   158  	if c != nil {
   159  		close(c)
   160  		delete(ts.cancellations, id)
   161  	}
   162  }