summaryrefslogtreecommitdiffstats
path: root/vespalib/src/vespa/vespalib/util/generationhandler.h
blob: d8af7f39c3e1740a78d48f6f93dbb6f78c2ee24d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#pragma once

#include <stdint.h>
#include <atomic>
#include <cassert>

namespace vespalib {

/**
 * Class used to keep track of the current generation of a component
 * (changed by a single writer), and previous generations still
 * occupied by multiple readers.  Readers will take a generation guard
 * by calling takeGuard().
 **/
class GenerationHandler {
public:
    typedef uint64_t generation_t;
    typedef int64_t sgeneration_t;

    /*
     * This must be type stable memory, and cannot be freed before the
     * GenerationHandler is freed (i.e. when external methods ensure that
     * no readers are still active).
     */
    class GenerationHold
    {
        // least significant bit is invalid flag
        std::atomic<uint32_t> _refCount;

        static bool valid(uint32_t refCount) { return (refCount & 1) == 0u; }
    public:
        generation_t _generation;
        GenerationHold *_next;	// next free element or next newer element.

        GenerationHold(void)
            : _refCount(1),
              _generation(0),
              _next(0)
        { }

        ~GenerationHold()
        {
            assert(getRefCount() == 0);
        }

        void setValid() {
            assert(!valid(_refCount));
            _refCount.fetch_sub(1);
        }
        bool setInvalid() {
            uint32_t refs = _refCount;
            assert(valid(refs));
            if (refs != 0) {
                return false;
            }
            return _refCount.compare_exchange_strong(refs, 1,
                                                     std::memory_order_seq_cst);
        }
        void release() { _refCount.fetch_sub(2); }
        GenerationHold *acquire() {
            if (valid(_refCount.fetch_add(2))) {
                return this;
            } else {
                release();
                return nullptr;
            }
        }
        static GenerationHold *copy(GenerationHold *self) {
            if (self == nullptr) {
                return nullptr;
            } else {
                uint32_t oldRefCount = self->_refCount.fetch_add(2);
                (void) oldRefCount;
                assert(valid(oldRefCount));
                return self;
            }
        }
        uint32_t getRefCount() const { return _refCount / 2; }
    };

    /**
     * Class that keeps a reference to a generation until destroyed.
     **/
    class Guard {
    private:
        GenerationHold *_hold;
        void cleanup() {
            if (_hold != nullptr) {
                _hold->release();
                _hold = nullptr;
            }
        }
    public:
        Guard();
        Guard(GenerationHold *hold); // hold is never nullptr
        ~Guard();
        Guard(const Guard & rhs);
        Guard(Guard &&rhs);
        Guard & operator=(const Guard & rhs);
        Guard & operator=(Guard &&rhs);

        bool valid(void) const {
            return _hold != nullptr;
        }
        generation_t getGeneration() const { return _hold->_generation; }
    };

private:
    generation_t _generation;
    generation_t _firstUsedGeneration;
    GenerationHold *_last;	// Points to "current generation" entry
    GenerationHold *_first;	// Points to "firstUsedGeneration" entry
    GenerationHold *_free;	// List of free entries
    uint32_t _numHolds;		// Number of allocated generation hold entries

public:
    /**
     * Creates a new generation handler.
     **/
    GenerationHandler();

    ~GenerationHandler();

    /**
     * Take a generation guard on the current generation.
     * Should be called by reader threads.
     **/
    Guard takeGuard() const;

    /**
     * Increases the current generation by 1.
     * Should be called by the writer thread.
     **/
    void incGeneration();

    /**
     * Update first used generation.
     * Should be called by the writer thread.
     */
    void updateFirstUsedGeneration();

    /**
     * Returns the first generation guarded by a reader.  It might be too low
     * if writer hasn't updated first used generation after last reader left.
     */
    generation_t getFirstUsedGeneration() const {
        return _firstUsedGeneration;
    }

    /**
     * Returns the current generation.
     **/
    generation_t getCurrentGeneration() const {
        return _generation;
    }

    generation_t getNextGeneration(void) const {
        return _generation + 1;
    }

    /**
     * Returns the number of readers holding a generation guard on the
     * given generation.  Should be called by the writer thread.
     */
    uint32_t getGenerationRefCount(generation_t gen) const;

    /**
     * Returns the number of readers holding a generation guard.
     * Should be called by the writer thread.
     */
    uint64_t getGenerationRefCount(void) const;

    /**
     * Returns true if we still have readers.  False positives and
     * negatives are possible if readers come and go while writer
     * updates generations.
     */
    bool hasReaders(void) const;
};

}