GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/fd_table.h Lines: 58 59 98.3 %
Date: 2019-02-03 02:48:13 Branches: 33 43 76.7 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 */
4
5
#ifndef CVMFS_FD_TABLE_H_
6
#define CVMFS_FD_TABLE_H_
7
8
#include <errno.h>
9
#include <inttypes.h>
10
#include <stdint.h>
11
12
#include <cassert>
13
#include <vector>
14
15
#include "util/single_copy.h"
16
17
/**
18
 * Maintains integers mapped to custom open file descriptors.  File descriptors
19
 * can be added, removed, and accessed.  All operations take constant time.  The
20
 * maximum file descriptor number needs to be known upfront.
21
 *
22
 * Note that new file descriptors do not necessarily have the smallest available
23
 * number but any number between 0..max_open_fds.
24
 *
25
 * This class is used by a couple of cache managers.
26
 */
27
template<class HandleT>
28
93
class FdTable : SingleCopy {
29
 public:
30
95
  FdTable(
31
    unsigned max_open_fds,
32
    const HandleT &invalid_handle)
33
    : invalid_handle_(invalid_handle)
34
    , fd_pivot_(0)
35
    , fd_index_(max_open_fds)
36
95
    , open_fds_(max_open_fds, FdWrapper(invalid_handle_, 0))
37
  {
38
95
    assert(max_open_fds > 0);
39
248307
    for (unsigned i = 0; i < max_open_fds; ++i) {
40
248212
      fd_index_[i] = i;
41
248212
      open_fds_[i].index = i;
42
    }
43
95
  }
44
45
  /**
46
   * Used to restore the state.
47
   */
48
2
  void AssignFrom(const FdTable<HandleT> &other) {
49
2
    invalid_handle_ = other.invalid_handle_;
50
2
    fd_pivot_ = other.fd_pivot_;
51
2
    fd_index_.resize(other.fd_index_.size());
52
2
    open_fds_.resize(other.open_fds_.size(), FdWrapper(invalid_handle_, 0));
53
258
    for (unsigned i = 0; i < fd_index_.size(); ++i) {
54
256
      fd_index_[i] = other.fd_index_[i];
55
256
      open_fds_[i] = other.open_fds_[i];
56
    }
57
2
  }
58
59
  /**
60
   * Used to save the state.
61
   */
62
2
  FdTable<HandleT> *Clone() {
63
    FdTable<HandleT> *result =
64
2
      new FdTable<HandleT>(open_fds_.size(), invalid_handle_);
65
2
    result->fd_pivot_ = fd_pivot_;
66
258
    for (unsigned i = 0; i < fd_index_.size(); ++i) {
67
256
      result->fd_index_[i] = fd_index_[i];
68
256
      result->open_fds_[i] = open_fds_[i];
69
    }
70
2
    return result;
71
  }
72
73
74
  /**
75
   * Registeres fd with a currently unused number.  If the table is full,
76
   * returns -ENFILE;
77
   */
78
1011477
  int OpenFd(const HandleT &handle) {
79
1011477
    if (handle == invalid_handle_)
80
      return -EINVAL;
81
1011477
    if (fd_pivot_ >= fd_index_.size())
82
167665
      return -ENFILE;
83
84
843812
    size_t next_fd = fd_index_[fd_pivot_];
85
843812
    assert(next_fd < open_fds_.size());
86
843812
    assert(open_fds_[next_fd].handle == invalid_handle_);
87
843812
    open_fds_[next_fd] = FdWrapper(handle, fd_pivot_);
88
843812
    ++fd_pivot_;
89
843812
    return next_fd;
90
  }
91
92
  /**
93
   * For invalid and unused numbers, the invalid handle is returned.
94
   */
95
1023129
  HandleT GetHandle(int fd) {
96
1023129
    return IsValid(fd) ? open_fds_[fd].handle : invalid_handle_;
97
  }
98
99
100
  /**
101
   * Releases fd back to the set of available numbers.  Gracefully handles
102
   * invalid handles (-EBADFD)
103
   */
104
1011674
  int CloseFd(int fd) {
105
1011674
    if (!IsValid(fd))
106
167888
      return -EBADF;
107
108
843786
    unsigned index = open_fds_[fd].index;
109
843786
    assert(index < fd_index_.size());
110
843786
    assert(fd_pivot_ <= fd_index_.size());
111
843786
    assert(fd_pivot_ > 0);
112
843786
    open_fds_[fd].handle = invalid_handle_;
113
843786
    --fd_pivot_;
114
843786
    if (index < fd_pivot_) {
115
461878
      unsigned other = fd_index_[fd_pivot_];
116
461878
      assert(other < open_fds_.size());
117
461878
      assert(open_fds_[other].handle != invalid_handle_);
118
461878
      open_fds_[other].index = index;
119
461878
      fd_index_[index] = other;
120
461878
      fd_index_[fd_pivot_] = fd;
121
    }
122
843786
    return 0;
123
  }
124
125
 private:
126
  struct FdWrapper {
127
843909
    FdWrapper(HandleT h, unsigned i) : handle(h), index(i) { }
128
129
    HandleT handle;
130
    /**
131
     * Back-pointer into fd_index_, which is needed when closing a file.
132
     */
133
    unsigned index;
134
  };
135
136
137
2034803
  inline bool IsValid(int fd) {
138

2034803
    if ((fd < 0) || (static_cast<unsigned>(fd) >= open_fds_.size()))
139
8
      return false;
140
2034795
    return open_fds_[fd].handle != invalid_handle_;
141
  }
142
143
  /**
144
   * An unused (available) file descriptor.
145
   */
146
  HandleT invalid_handle_;
147
  /**
148
   * The index of the first available file descriptor in fd_index_.
149
   */
150
  unsigned fd_pivot_;
151
  /**
152
   * Maps into open_fds_.  Until fd_pivot_, file descriptors are used.  As of
153
   * fd_pivot_, points to free file descriptors.  Used to acquire new file
154
   * descriptors in constant time.
155
   */
156
  std::vector<unsigned> fd_index_;
157
  /**
158
   * The file descriptor number mapped to a user-defined file descriptor
159
   * (struct).  The fd integer passed to users of the file descriptor table
160
   * points into this array.
161
   */
162
  std::vector<FdWrapper> open_fds_;
163
};  // class FdTable
164
165
#endif  // CVMFS_FD_TABLE_H_