github.com/tursom/GoCollections@v0.3.10/concurrent/collections/PublisherMessageQueue.go (about) 1 /* 2 * Copyright (c) 2022 tursom. All rights reserved. 3 * Use of this source code is governed by a GPL-3 4 * license that can be found in the LICENSE file. 5 */ 6 7 package collections 8 9 import ( 10 "log" 11 "sync" 12 13 "github.com/tursom/GoCollections/util/time" 14 15 "github.com/tursom/GoCollections/concurrent" 16 "github.com/tursom/GoCollections/exceptions" 17 "github.com/tursom/GoCollections/lang" 18 "github.com/tursom/GoCollections/lang/atomic" 19 ) 20 21 type ( 22 // PublisherMessageQueue 23 // Enable an application to announce events to multiple interested consumers asynchronously, 24 // without coupling the senders to the receivers 25 PublisherMessageQueue[T any] struct { 26 end *publisherMessageQueueNode[T] 27 lock sync.Mutex 28 cond concurrent.Cond 29 } 30 31 publisherMessageQueueNode[T any] struct { 32 index int 33 value T 34 next *publisherMessageQueueNode[T] 35 } 36 ) 37 38 func (q *PublisherMessageQueue[T]) getEnd() *publisherMessageQueueNode[T] { 39 if q.end == nil { 40 q.lock.Lock() 41 defer q.lock.Unlock() 42 if q.end == nil { 43 q.end = &publisherMessageQueueNode[T]{} 44 } 45 } 46 return q.end 47 } 48 49 func (q *PublisherMessageQueue[T]) getCond() concurrent.Cond { 50 if q.cond == nil { 51 q.lock.Lock() 52 defer q.lock.Unlock() 53 q.cond = concurrent.NewCond(&q.lock) 54 } 55 return q.cond 56 } 57 58 func (q *PublisherMessageQueue[T]) Subscribe() lang.ReceiveChannel[T] { 59 end := q.getEnd() 60 ch := lang.NewChannel[T](0) 61 canceled := false 62 go func() { 63 defer ch.Close() 64 65 cond := q.getCond() 66 node := &end.next 67 for !canceled { 68 // node may be nil when MQ created 69 for *node != nil { 70 if canceled { 71 return 72 } 73 for !ch.SendTimeout((*node).value, time.Second) && !canceled { 74 // check MessageQueueCapacity 75 if MessageQueueCapacity != -1 { 76 continue 77 } 78 diff := q.end.index - (*node).index 79 if diff >= MessageQueueWarnLimit { 80 log.Printf("MD is on warn stack") 81 } 82 if diff > MessageQueueCapacity { 83 panic(exceptions.NewIndexOutOfBound("object buffer of this MQ is full", nil)) 84 } 85 } 86 node = &(*node).next 87 } 88 cond.Wait() 89 } 90 }() 91 return lang.WithReceiveChannel[T](ch, func() { 92 canceled = true 93 }) 94 } 95 96 func (q *PublisherMessageQueue[T]) Send(msg T) { 97 index := 0 98 if q.end != nil { 99 index = q.end.index + 1 100 } 101 102 node := &publisherMessageQueueNode[T]{ 103 index: index, 104 value: msg, 105 } 106 107 p := &q.getEnd().next 108 for !atomic.CompareAndSwapPointer(p, nil, node) { 109 for *p != nil { 110 p = &q.end.next 111 } 112 node.index = q.end.index + 1 113 } 114 q.end = node 115 q.getCond().Broadcast() 116 }