nanomsg.org/go/mangos/v2@v2.0.9-0.20200203084354-8a092611e461/message.go (about) 1 // Copyright 2019 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 ) 21 22 // Message encapsulates the messages that we exchange back and forth. The 23 // meaning of the Header and Body fields, and where the splits occur, will 24 // vary depending on the protocol. Note however that any headers applied by 25 // transport layers (including TCP/ethernet headers, and SP protocol 26 // independent length headers), are *not* included in the Header. 27 type Message struct { 28 // Header carries any protocol (SP) specific header. Applications 29 // should not modify or use this unless they are using Raw mode. 30 // No user data may be placed here. 31 Header []byte 32 33 // Body carries the body of the message. This can also be thought 34 // of as the message "payload". 35 Body []byte 36 37 // Pipe may be set on message receipt, to indicate the Pipe from 38 // which the Message was received. There are no guarantees that the 39 // Pipe is still active, and applications should only use this for 40 // informational purposes. 41 Pipe Pipe 42 43 bbuf []byte 44 hbuf []byte 45 bsize int 46 refcnt int32 47 } 48 49 type msgCacheInfo struct { 50 maxbody int 51 pool *sync.Pool 52 } 53 54 func newMsg(sz int) *Message { 55 m := &Message{} 56 m.bbuf = make([]byte, 0, sz) 57 m.hbuf = make([]byte, 0, 32) 58 m.bsize = sz 59 return m 60 } 61 62 // We can tweak these! 63 var messageCache = []msgCacheInfo{ 64 { 65 maxbody: 64, 66 pool: &sync.Pool{ 67 New: func() interface{} { return newMsg(64) }, 68 }, 69 }, { 70 maxbody: 128, 71 pool: &sync.Pool{ 72 New: func() interface{} { return newMsg(128) }, 73 }, 74 }, { 75 maxbody: 256, 76 pool: &sync.Pool{ 77 New: func() interface{} { return newMsg(256) }, 78 }, 79 }, { 80 maxbody: 512, 81 pool: &sync.Pool{ 82 New: func() interface{} { return newMsg(512) }, 83 }, 84 }, { 85 maxbody: 1024, 86 pool: &sync.Pool{ 87 New: func() interface{} { return newMsg(1024) }, 88 }, 89 }, { 90 maxbody: 4096, 91 pool: &sync.Pool{ 92 New: func() interface{} { return newMsg(4096) }, 93 }, 94 }, { 95 maxbody: 8192, 96 pool: &sync.Pool{ 97 New: func() interface{} { return newMsg(8192) }, 98 }, 99 }, { 100 maxbody: 65536, 101 pool: &sync.Pool{ 102 New: func() interface{} { return newMsg(65536) }, 103 }, 104 }, 105 } 106 107 // Free releases the message to the pool from which it was allocated. 108 // While this is not strictly necessary thanks to GC, doing so allows 109 // for the resources to be recycled without engaging GC. This can have 110 // rather substantial benefits for performance. 111 func (m *Message) Free() { 112 if m != nil { 113 if atomic.AddInt32(&m.refcnt, -1) == 0 { 114 for i := range messageCache { 115 if m.bsize == messageCache[i].maxbody { 116 messageCache[i].pool.Put(m) 117 return 118 } 119 } 120 } 121 } 122 } 123 124 // Clone bumps the reference count on the message, allowing it to be 125 // shared. Callers of this MUST ensure that the message is never modified. 126 // If a read-only copy needs to be made "unique", callers can do so by 127 // using the Uniq function. 128 func (m *Message) Clone() { 129 atomic.AddInt32(&m.refcnt, 1) 130 } 131 132 // MakeUnique ensures that the message is not shared. If the reference 133 // count on the message is one, then the message is returned as is. 134 // Otherwise a new copy of hte message is made, and the reference count 135 // on the original is dropped. Note that it is an error for the caller 136 // to use the original message after this function; the caller should 137 // always do `m = m.MakeUnique()`. This function should be called whenever 138 // the message is leaving the control of the caller, such as when passing 139 // it to a user program. 140 // 141 // Note that transports always should call this on their transmit path 142 // if they are going to modify the message. (Most do not.) 143 func (m *Message) MakeUnique() *Message { 144 if atomic.LoadInt32(&m.refcnt) == 1 { 145 return m 146 } 147 d := m.Dup() 148 m.Free() 149 return d 150 } 151 152 // 153 154 // Dup creates a "duplicate" message. The message is made as a 155 // deep copy, so the resulting message is safe to modify. 156 func (m *Message) Dup() *Message { 157 dup := NewMessage(len(m.Body)) 158 dup.Body = append(dup.Body, m.Body...) 159 dup.Header = append(dup.Header, m.Header...) 160 dup.Pipe = m.Pipe 161 return dup 162 } 163 164 // NewMessage is the supported way to obtain a new Message. This makes 165 // use of a "cache" which greatly reduces the load on the garbage collector. 166 func NewMessage(sz int) *Message { 167 var m *Message 168 for i := range messageCache { 169 if sz < messageCache[i].maxbody { 170 m = messageCache[i].pool.Get().(*Message) 171 break 172 } 173 } 174 if m == nil { 175 m = newMsg(sz) 176 } 177 178 m.Body = m.bbuf 179 m.Header = m.hbuf 180 atomic.StoreInt32(&m.refcnt, 1) 181 return m 182 }