github.com/sunvim/utils@v0.1.0/lamport/mem_clock.go (about) 1 /* 2 3 This Source Code Form is subject to the terms of the Mozilla Public 4 License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 Copyright (c) 2013, Armon Dadgar armon.dadgar@gmail.com 8 Copyright (c) 2013, Mitchell Hashimoto mitchell.hashimoto@gmail.com 9 10 Alternatively, the contents of this file may be used under the terms 11 of the GNU General Public License Version 3 or later, as described below: 12 13 This file is free software: you may copy, redistribute and/or modify 14 it under the terms of the GNU General Public License as published by the 15 Free Software Foundation, either version 3 of the License, or (at your 16 option) any later version. 17 18 This file is distributed in the hope that it will be useful, but 19 WITHOUT ANY WARRANTY; without even the implied warranty of 20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 21 Public License for more details. 22 23 You should have received a copy of the GNU General Public License 24 along with this program. If not, see http://www.gnu.org/licenses/. 25 26 */ 27 28 // Note: this code originally originate from Hashicorp's Serf but has been changed since to fit git-bug's need. 29 30 // Note: this Lamport clock implementation is different than the algorithms you can find, notably Wikipedia or the 31 // original Serf implementation. The reason is lie to what constitute an event in this distributed system. 32 // Commonly, events happen when messages are sent or received, whereas in git-bug events happen when some data is 33 // written, but *not* when read. This is why Witness set the time to the max seen value instead of max seen value +1. 34 // See https://cs.stackexchange.com/a/133730/129795 35 36 package lamport 37 38 import ( 39 "sync/atomic" 40 ) 41 42 var _ Clock = &MemClock{} 43 44 // MemClock is a thread safe implementation of a lamport clock. It 45 // uses efficient atomic operations for all of its functions, falling back 46 // to a heavy lock only if there are enough CAS failures. 47 type MemClock struct { 48 counter uint64 49 } 50 51 // NewMemClock create a new clock with the value 1. 52 // Value 0 is considered as invalid. 53 func NewMemClock() *MemClock { 54 return &MemClock{ 55 counter: 1, 56 } 57 } 58 59 // NewMemClockWithTime create a new clock with a value. 60 func NewMemClockWithTime(time uint64) *MemClock { 61 return &MemClock{ 62 counter: time, 63 } 64 } 65 66 // Time is used to return the current value of the lamport clock 67 func (mc *MemClock) Time() Time { 68 return Time(atomic.LoadUint64(&mc.counter)) 69 } 70 71 // Increment is used to return the value of the lamport clock and increment it afterwards 72 func (mc *MemClock) Increment() (Time, error) { 73 return Time(atomic.AddUint64(&mc.counter, 1)), nil 74 } 75 76 // Witness is called to update our local clock if necessary after 77 // witnessing a clock value received from another process 78 func (mc *MemClock) Witness(v Time) error { 79 WITNESS: 80 // If the other value is old, we do not need to do anything 81 cur := atomic.LoadUint64(&mc.counter) 82 other := uint64(v) 83 if other <= cur { 84 return nil 85 } 86 87 // Ensure that our local clock is at least one ahead. 88 if !atomic.CompareAndSwapUint64(&mc.counter, cur, other) { 89 // CAS: CompareAndSwap 90 // The CAS failed, so we just retry. Eventually our CAS should 91 // succeed or a future witness will pass us by and our witness 92 // will end. 93 goto WITNESS 94 } 95 96 return nil 97 }