github.com/gdamore/mangos@v1.4.0/message.go (about) 1 // Copyright 2016 The Mangos Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use file except in compliance with the License. 5 // You may obtain a copy of the license at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mangos 16 17 import ( 18 "sync" 19 "sync/atomic" 20 "time" 21 ) 22 23 // Message encapsulates the messages that we exchange back and forth. The 24 // meaning of the Header and Body fields, and where the splits occur, will 25 // vary depending on the protocol. Note however that any headers applied by 26 // transport layers (including TCP/ethernet headers, and SP protocol 27 // independent length headers), are *not* included in the Header. 28 type Message struct { 29 // Header carries any protocol (SP) specific header. Applications 30 // should not modify or use this unless they are using Raw mode. 31 // No user data may be placed here. 32 Header []byte 33 34 // Body carries the body of the message. This can also be thought 35 // of as the message "payload". 36 Body []byte 37 38 // Port may be set on message receipt, to indicate the Port from 39 // which the Message was received. There are no guarantees that the 40 // Port is still active, and applications should only use this for 41 // informational purposes. 42 Port Port 43 44 bbuf []byte 45 hbuf []byte 46 bsize int 47 refcnt int32 48 expire time.Time 49 pool *sync.Pool 50 } 51 52 type msgCacheInfo struct { 53 maxbody int 54 pool *sync.Pool 55 } 56 57 func newMsg(sz int) *Message { 58 m := &Message{} 59 m.bbuf = make([]byte, 0, sz) 60 m.hbuf = make([]byte, 0, 32) 61 m.bsize = sz 62 return m 63 } 64 65 // We can tweak these! 66 var messageCache = []msgCacheInfo{ 67 { 68 maxbody: 64, 69 pool: &sync.Pool{ 70 New: func() interface{} { return newMsg(64) }, 71 }, 72 }, { 73 maxbody: 128, 74 pool: &sync.Pool{ 75 New: func() interface{} { return newMsg(128) }, 76 }, 77 }, { 78 maxbody: 256, 79 pool: &sync.Pool{ 80 New: func() interface{} { return newMsg(256) }, 81 }, 82 }, { 83 maxbody: 512, 84 pool: &sync.Pool{ 85 New: func() interface{} { return newMsg(512) }, 86 }, 87 }, { 88 maxbody: 1024, 89 pool: &sync.Pool{ 90 New: func() interface{} { return newMsg(1024) }, 91 }, 92 }, { 93 maxbody: 4096, 94 pool: &sync.Pool{ 95 New: func() interface{} { return newMsg(4096) }, 96 }, 97 }, { 98 maxbody: 8192, 99 pool: &sync.Pool{ 100 New: func() interface{} { return newMsg(8192) }, 101 }, 102 }, { 103 maxbody: 65536, 104 pool: &sync.Pool{ 105 New: func() interface{} { return newMsg(65536) }, 106 }, 107 }, 108 } 109 110 // Free decrements the reference count on a message, and releases its 111 // resources if no further references remain. While this is not 112 // strictly necessary thanks to GC, doing so allows for the resources to 113 // be recycled without engaging GC. This can have rather substantial 114 // benefits for performance. 115 func (m *Message) Free() { 116 if v := atomic.AddInt32(&m.refcnt, -1); v > 0 { 117 return 118 } 119 for i := range messageCache { 120 if m.bsize == messageCache[i].maxbody { 121 messageCache[i].pool.Put(m) 122 return 123 } 124 } 125 } 126 127 // Dup creates a "duplicate" message. What it really does is simply 128 // increment the reference count on the message. Note that since the 129 // underlying message is actually shared, consumers must take care not 130 // to modify the message. (We might revise this API in the future to 131 // add a copy-on-write facility, but for now modification is neither 132 // needed nor supported.) Applications should *NOT* make use of this 133 // function -- it is intended for Protocol, Transport and internal use only. 134 func (m *Message) Dup() *Message { 135 atomic.AddInt32(&m.refcnt, 1) 136 return m 137 } 138 139 // Expired returns true if the message has "expired". This is used by 140 // transport implementations to discard messages that have been 141 // stuck in the write queue for too long, and should be discarded rather 142 // than delivered across the transport. This is only used on the TX 143 // path, there is no sense of "expiration" on the RX path. 144 func (m *Message) Expired() bool { 145 if m.expire.IsZero() { 146 return false 147 } 148 if m.expire.After(time.Now()) { 149 return false 150 } 151 return true 152 } 153 154 // NewMessage is the supported way to obtain a new Message. This makes 155 // use of a "cache" which greatly reduces the load on the garbage collector. 156 func NewMessage(sz int) *Message { 157 var m *Message 158 for i := range messageCache { 159 if sz < messageCache[i].maxbody { 160 m = messageCache[i].pool.Get().(*Message) 161 break 162 } 163 } 164 if m == nil { 165 m = newMsg(sz) 166 } 167 168 m.refcnt = 1 169 m.Body = m.bbuf 170 m.Header = m.hbuf 171 return m 172 }