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  }