Advertisement
dvulakh

concurrent.go

Mar 3rd, 2020
856
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Go 5.06 KB | None | 0 0
  1. package concurrent
  2.  
  3. import (
  4.     "container/list"
  5.     "sync/atomic"
  6.     "unsafe"
  7.     "sync"
  8. )
  9.  
  10. /*** TYPE DEFINITIONS ***/
  11.  
  12. // Node is an element of a concurrent collection.
  13. // Node stores a value of type interface{} and pointers to the following node.
  14. type Node struct {
  15.     // Val is the value of type interface{} located in the position represented by the Node structure.
  16.     Val interface{}
  17.     // Nxt is a pointer to the following Node.
  18.     // Nxt is nil for the last position of a collection (the bottom of a stack and the tail of a queue).
  19.     Nxt unsafe.Pointer
  20. }
  21.  
  22. // ConcurrentCollection is an interface that ConcurrentStack and ConcurrentQueue implement.
  23. // ConcurrentCollection requires that structures posses pointers to 'head' and 'tail' elements.
  24. type ConcurrentCollection interface {
  25.     Head()  *unsafe.Pointer
  26.     Tail()  *unsafe.Pointer
  27. }
  28.  
  29. // ConcurrentStack is a collection that can be concurrently accessed.
  30. // Elements are accessed in FILO order.
  31. // ConcurrentStack stores the top of the stack.
  32. type ConcurrentStack struct {
  33.     // Top is a pointer to the Node representing the top of the stack.
  34.     Top unsafe.Pointer
  35. }
  36.  
  37. // MultiMap is a collection that can be concurrently accessed.
  38. // Elements are accessed with a key of type interface{}.
  39. // Locks are utilized to maintain atomic nature.
  40. type MultiMap struct {
  41.     // Wmap is a pointer to the wrapped map.
  42.     Wmap    map[interface{}]*list.List
  43.     // MapLock is a mutex that locks during map accessing/editing.
  44.     MapLock sync.Mutex
  45.     // ExLock is a lock that can be used externally.
  46.     ExLock  sync.Mutex
  47. }
  48.  
  49. /*** NODE IMPLEMENTATION ***/
  50.  
  51. // A Node is constructed from an element value.
  52. // Nxt and Prv are both initialized to nil.
  53. func newNode(e interface{}) unsafe.Pointer {
  54.     n := new(Node)
  55.     n.Val = e
  56.     n.Nxt = nil
  57.     return unsafe.Pointer(n)
  58. }
  59.  
  60. /*** CONCURRENTCOLLECTION IMPLEMENTATION ***/
  61.  
  62. // Push inserts a new element e of type interface{} to the tail of a ConcurrentCollection ch.
  63. // The tail pointer is moved to point to the newest element.
  64. func Push(ch ConcurrentCollection, e interface{}) {
  65.     n := newNode(e)
  66.     for swapped := false; !swapped; swapped = atomic.CompareAndSwapPointer(ch.Tail(), (*Node)(n).Nxt, n) {
  67.         (*Node)(n).Nxt = *ch.Tail()
  68.     }
  69. }
  70.  
  71. // Peek returns the value of the current head of a ConcurrentCollection ch.
  72. // Peek does not alter the ConcurrentCollection structure.
  73. func Peek(ch ConcurrentCollection) interface{} {
  74.     n := *ch.Head()
  75.     if n != nil {
  76.         return (*Node)(n).Val
  77.     }
  78.     return nil
  79. }
  80.  
  81. // Pop returns the 'top' element of the stack and deletes the corresponding node.
  82. // The stack top pointer is advanced to the next element.
  83. func Pop(ch ConcurrentCollection) interface{} {
  84.     n := *ch.Head()
  85.     var x unsafe.Pointer
  86.     if (*Node)(n) != nil {
  87.         x = (*Node)(n).Nxt
  88.     } else {
  89.         x = unsafe.Pointer(nil)
  90.     }
  91.     for swapped := false; !swapped; swapped = atomic.CompareAndSwapPointer(ch.Head(), n, x) {
  92.         n = *ch.Head()
  93.         if (*Node)(n) != nil {
  94.             x = (*Node)(n).Nxt
  95.         } else {
  96.             x = unsafe.Pointer(nil)
  97.         }
  98.     }
  99.     if (*Node)(n) == nil {
  100.         return nil
  101.     }
  102.     return (*Node)(n).Val
  103. }
  104.  
  105. /*** STACK IMPLEMENTATION ***/
  106.  
  107. // Head returns the top of the stack.
  108. func (s *ConcurrentStack) Head() *unsafe.Pointer { return &s.Top }
  109.  
  110. // Tail returns the top of the stack.
  111. func (s *ConcurrentStack) Tail() *unsafe.Pointer { return &s.Top }
  112.  
  113. // NewStack constructs a new stack with no elements.
  114. func NewStack() *ConcurrentStack {
  115.     s := new(ConcurrentStack)
  116.     s.Top = unsafe.Pointer(nil)
  117.     return s
  118. }
  119.  
  120. /*** MAP IMPLEMENTATION ***/
  121.  
  122. func NewMap() *MultiMap {
  123.     m := new(MultiMap)
  124.     m.Wmap = make(map[interface{}]*list.List)
  125.     return m
  126. }
  127.  
  128. // Contains takes as arguments values k and v.
  129. // Contains returns true if v is a value associated with key k and false otherwise.
  130. // The coupling parameter coup can be carefully used to atomically 'couple' several calls to different map methods.
  131. func (m *MultiMap) Contains(k, v interface{}, coup ...bool) bool {
  132.     if !(len(coup) > 0 && coup[0]) {
  133.         m.MapLock.Lock()
  134.     }
  135.     l := m.Wmap[k]
  136.     if l == nil {
  137.         return false
  138.     }
  139.     for e := l.Front(); e != nil; e = e.Next() {
  140.         if e.Value == v {
  141.             if !(len(coup) > 1 && coup[1]) {
  142.                 m.MapLock.Unlock()
  143.             }
  144.             return true
  145.         }
  146.     }
  147.     if !(len(coup) > 1 && coup[1]) {
  148.         m.MapLock.Unlock()
  149.     }
  150.     return false
  151. }
  152.  
  153. // Insert takes as arguments values k and v.
  154. // Insert adds v as one of the values associated with k.
  155. // The coupling parameter coup can be carefully used to atomically 'couple' several calls to different map methods.
  156. func (m *MultiMap) Insert(k, v interface{}, coup ...bool) {
  157.     if !(len(coup) > 0 && coup[0]) {
  158.         m.MapLock.Lock()
  159.     }
  160.     if m.Wmap[k] == nil {
  161.         m.Wmap[k] = list.New()
  162.     }
  163.     m.Wmap[k].PushBack(v)
  164.     if !(len(coup) > 1 && coup[1]) {
  165.         m.MapLock.Unlock()
  166.     }
  167. }
  168.  
  169. // Erase erases all associations to key k.
  170. // The coupling parameter coup can be carefully used to atomically 'couple' several calls to different map methods.
  171. func (m *MultiMap) Erase(k interface{}, coup ...bool) {
  172.     if !(len(coup) > 0 && coup[0]) {
  173.         m.MapLock.Lock()
  174.     }
  175.     delete(m.Wmap, k)
  176.     if !(len(coup) > 1 && coup[1]) {
  177.         m.MapLock.Unlock()
  178.     }
  179. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement