github.com/m3db/m3@v1.5.0/src/metrics/matcher/cache/list.go (about)

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package cache
    22  
    23  import (
    24  	"sync"
    25  	"sync/atomic"
    26  	"time"
    27  
    28  	"github.com/m3db/m3/src/metrics/rules"
    29  )
    30  
    31  // element is a list element
    32  type element struct {
    33  	namespace   []byte
    34  	id          []byte
    35  	result      rules.MatchResult
    36  	deleted     bool
    37  	expiryNanos int64
    38  	prev        *element
    39  	next        *element
    40  }
    41  
    42  func newElement(
    43  	namespace, id []byte,
    44  	result rules.MatchResult,
    45  ) *element {
    46  	return &element{
    47  		namespace: append([]byte(nil), namespace...),
    48  		id:        append([]byte(nil), id...),
    49  		result:    result,
    50  	}
    51  }
    52  
    53  // ShouldPromote determines whether the previous promotion has expired
    54  // and we should perform a new promotion.
    55  func (e *element) ShouldPromote(now time.Time) bool {
    56  	return atomic.LoadInt64(&e.expiryNanos) <= now.UnixNano()
    57  }
    58  
    59  // SetPromotionExpiry sets the expiry time of the current promotion.
    60  func (e *element) SetPromotionExpiry(t time.Time) {
    61  	atomic.StoreInt64(&e.expiryNanos, t.UnixNano())
    62  }
    63  
    64  // list is a type-specific implementation of doubly linked lists consisting
    65  // of elements so when we retrieve match results, there is no conversion
    66  // between interface{} and the concrete result type.
    67  type list struct {
    68  	head   *element
    69  	tail   *element
    70  	length int
    71  }
    72  
    73  // Len returns the number of elements in the list.
    74  func (l *list) Len() int { return l.length }
    75  
    76  // Front returns the first element in the list.
    77  func (l *list) Front() *element { return l.head }
    78  
    79  // Back returns the last element in the list.
    80  func (l *list) Back() *element { return l.tail }
    81  
    82  // PushFront pushes an element to the front of the list.
    83  func (l *list) PushFront(elem *element) {
    84  	if elem == nil || elem.deleted {
    85  		return
    86  	}
    87  	if l.head == nil {
    88  		l.head = elem
    89  		l.tail = elem
    90  		elem.prev = nil
    91  		elem.next = nil
    92  	} else {
    93  		elem.next = l.head
    94  		elem.prev = nil
    95  		l.head.prev = elem
    96  		l.head = elem
    97  	}
    98  	l.length++
    99  }
   100  
   101  // PushBack pushes an element to the back of the list.
   102  func (l *list) PushBack(elem *element) {
   103  	if elem == nil || elem.deleted {
   104  		return
   105  	}
   106  	if l.tail == nil {
   107  		l.head = elem
   108  		l.tail = elem
   109  		elem.prev = nil
   110  		elem.next = nil
   111  	} else {
   112  		elem.prev = l.tail
   113  		elem.next = nil
   114  		l.tail.next = elem
   115  		l.tail = elem
   116  	}
   117  	l.length++
   118  }
   119  
   120  // Remove removes an element from the list.
   121  func (l *list) Remove(elem *element) {
   122  	if elem == nil || elem.deleted {
   123  		return
   124  	}
   125  
   126  	// Remove the element from the list.
   127  	l.remove(elem, true)
   128  }
   129  
   130  // MoveToFront moves an element to the beginning of the list.
   131  func (l *list) MoveToFront(elem *element) {
   132  	if elem == nil || elem.deleted {
   133  		return
   134  	}
   135  
   136  	// Removing the element from the list.
   137  	l.remove(elem, false)
   138  
   139  	// Pushing the element to the front of the list.
   140  	l.PushFront(elem)
   141  }
   142  
   143  func (l *list) remove(elem *element, markDeleted bool) {
   144  	prev := elem.prev
   145  	next := elem.next
   146  	if prev == nil {
   147  		l.head = next
   148  	} else {
   149  		prev.next = next
   150  	}
   151  	if next == nil {
   152  		l.tail = prev
   153  	} else {
   154  		next.prev = prev
   155  	}
   156  	l.length--
   157  	elem.prev = nil // avoid memory leak.
   158  	elem.next = nil // avoid memory leak.
   159  	elem.deleted = markDeleted
   160  }
   161  
   162  type lockedList struct {
   163  	sync.Mutex
   164  	list
   165  }