Read/write loop test working
This commit is contained in:
151
.clang-format
Normal file
151
.clang-format
Normal file
@@ -0,0 +1,151 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: Google
|
||||
AccessModifierOffset: -1
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^<ext/.*\.h>'
|
||||
Priority: 2
|
||||
- Regex: '^<.*\.h>'
|
||||
Priority: 1
|
||||
- Regex: '^<.*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 2
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*.a
|
||||
*.so
|
||||
*.o
|
||||
*.swp
|
||||
38
Makefile
Normal file
38
Makefile
Normal file
@@ -0,0 +1,38 @@
|
||||
FIRE_CXX ?= clang++
|
||||
FIRE_CXXFLAGS ?= -O3 -std=gnu++2a -Wall -Werror -Wextra -fPIE -fPIC -fstack-protector-strong -fsanitize=safe-stack -fsanitize=safe-stack
|
||||
FIRE_LDFLAGS ?= -fuse-ld=gold -flto -Wl,-z,relro -Wl,-z,now
|
||||
FIRE_LDLIBS ?= -lglog -lgflags -luring
|
||||
|
||||
all: liburing++.a liburing++.o liburing++.so test_loop
|
||||
|
||||
objects = uring.o
|
||||
|
||||
liburing++.a: $(objects)
|
||||
ar rcs $@ $^
|
||||
|
||||
liburing++.o: $(objects)
|
||||
gold -z relro -z now -r --output=$@ $+
|
||||
|
||||
liburing++.so: $(objects)
|
||||
$(FIRE_CXX) $(FIRE_CXXFLAGS) $(FIRE_LDFLAGS) -shared -o $@ $+ $(FIRE_LDLIBS)
|
||||
|
||||
test_loop: test_loop.o uring.o
|
||||
$(FIRE_CXX) $(FIRE_CXXFLAGS) $(FIRE_LDFLAGS) -pie -o $@ $+ $(FIRE_LDLIBS)
|
||||
|
||||
%.o: %.cc *.h Makefile
|
||||
$(FIRE_CXX) $(FIRE_CXXFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm --force *.so *.o *.a
|
||||
|
||||
asan:
|
||||
$(MAKE) clean
|
||||
FIRE_CXXFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer -std=gnu++2a -fPIE -fPIC" $(MAKE) all
|
||||
|
||||
tsan:
|
||||
$(MAKE) clean
|
||||
FIRE_CXXFLAGS="-O1 -g -fsanitize=thread -std=gnu++2a -fPIE -fPIC" $(MAKE) all
|
||||
|
||||
ubsan:
|
||||
$(MAKE) clean
|
||||
FIRE_CXXFLAGS="-O1 -g -fsanitize=undefined -std=gnu++2a -fPIE -fPIC" $(MAKE) all
|
||||
1
README.md
Normal file
1
README.md
Normal file
@@ -0,0 +1 @@
|
||||
Idiomatic C++ wrapper around [liburing](http://git.kernel.dk/cgit/liburing/)
|
||||
40
test_loop.cc
Normal file
40
test_loop.cc
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "uring.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
uring::URing uring(1);
|
||||
|
||||
int sv[2];
|
||||
PCHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == 0);
|
||||
|
||||
std::string foo("foo\n");
|
||||
|
||||
bool fired = false;
|
||||
uring.write(sv[0], foo.data(), foo.size(), [&](int32_t res) {
|
||||
fired = true;
|
||||
LOG(INFO) << "write() res=" << res;
|
||||
CHECK_EQ(res, 4);
|
||||
});
|
||||
uring.Submit();
|
||||
uring.Wait();
|
||||
CHECK(fired);
|
||||
|
||||
char buf[10];
|
||||
fired = false;
|
||||
uring.read(sv[1], buf, 10, [&](int32_t res) {
|
||||
fired = true;
|
||||
LOG(INFO) << "read() res=" << res;
|
||||
CHECK_EQ(res, 4);
|
||||
CHECK_EQ(memcmp(buf, foo.data(), foo.size()), 0);
|
||||
});
|
||||
uring.Submit();
|
||||
uring.Wait();
|
||||
CHECK(fired);
|
||||
|
||||
CHECK_EQ(uring.Try(), false);
|
||||
}
|
||||
122
uring.cc
Normal file
122
uring.cc
Normal file
@@ -0,0 +1,122 @@
|
||||
#include "uring.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
namespace uring {
|
||||
|
||||
URing::URing(uint32_t num_entries)
|
||||
: ring_fd_(io_uring_queue_init(num_entries, &ring_, 0)),
|
||||
entries_(num_entries * 3),
|
||||
free_head_(&entries_.at(0)) {
|
||||
PCHECK(ring_fd_ >= 0) << "io_uring_queue_init()";
|
||||
|
||||
// Chain the entries together into a list that we'll use as a free list for
|
||||
// future allocation.
|
||||
Entry *prev = nullptr;
|
||||
for (auto &entry : entries_) {
|
||||
entry.free_next = prev;
|
||||
prev = &entry;
|
||||
}
|
||||
}
|
||||
|
||||
URing::~URing() { io_uring_queue_exit(&ring_); }
|
||||
|
||||
void URing::Submit() { io_uring_submit(&ring_); }
|
||||
|
||||
void URing::Wait() {
|
||||
io_uring_cqe *cqe;
|
||||
PCHECK(io_uring_wait_cqe(&ring_, &cqe) == 0);
|
||||
ProcessCQE(cqe);
|
||||
}
|
||||
|
||||
bool URing::Try() {
|
||||
io_uring_cqe *cqe = nullptr;
|
||||
PCHECK(io_uring_peek_cqe(&ring_, &cqe) == 0);
|
||||
if (cqe) {
|
||||
ProcessCQE(cqe);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void URing::write(int fd, const void *buf, size_t count,
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
std::vector<iovec> vecs{
|
||||
{
|
||||
.iov_base = const_cast<void *>(buf),
|
||||
.iov_len = count,
|
||||
},
|
||||
};
|
||||
writev(fd, vecs, callback);
|
||||
}
|
||||
|
||||
void URing::writev(int fd, const std::vector<iovec> &vecs,
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
pwritev(fd, vecs, 0, callback);
|
||||
}
|
||||
|
||||
void URing::pwritev(int fd, const std::vector<iovec> &vecs, off_t offset,
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
auto *entry = GetEntry(callback);
|
||||
entry->vecs = vecs;
|
||||
|
||||
auto *sqe = GetSQE();
|
||||
io_uring_prep_writev(sqe, fd, entry->vecs.data(), entry->vecs.size(), offset);
|
||||
io_uring_sqe_set_data(sqe, reinterpret_cast<void *>(entry));
|
||||
}
|
||||
|
||||
void URing::read(int fd, const void *buf, size_t count,
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
std::vector<iovec> vecs{
|
||||
{
|
||||
.iov_base = const_cast<void *>(buf),
|
||||
.iov_len = count,
|
||||
},
|
||||
};
|
||||
readv(fd, vecs, callback);
|
||||
}
|
||||
|
||||
void URing::readv(int fd, const std::vector<iovec> &vecs,
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
preadv(fd, vecs, 0, callback);
|
||||
}
|
||||
|
||||
void URing::preadv(int fd, const std::vector<iovec> &vecs, off_t offset,
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
auto *entry = GetEntry(callback);
|
||||
entry->vecs = vecs;
|
||||
|
||||
auto *sqe = GetSQE();
|
||||
io_uring_prep_readv(sqe, fd, entry->vecs.data(), entry->vecs.size(), offset);
|
||||
io_uring_sqe_set_data(sqe, reinterpret_cast<void *>(entry));
|
||||
}
|
||||
|
||||
URing::Entry *URing::GetEntry(
|
||||
const std::function<void(int32_t res)> &callback) {
|
||||
CHECK(free_head_);
|
||||
auto *entry = free_head_;
|
||||
free_head_ = entry->free_next;
|
||||
entry->callback = callback;
|
||||
return entry;
|
||||
}
|
||||
|
||||
void URing::PutEntry(Entry *entry, int32_t res) {
|
||||
entry->callback(res);
|
||||
entry->free_next = free_head_;
|
||||
free_head_ = entry;
|
||||
}
|
||||
|
||||
io_uring_sqe *URing::GetSQE() {
|
||||
auto *sqe = io_uring_get_sqe(&ring_);
|
||||
// TODO: automatically Submit() on full submit queue? spin? something else?
|
||||
CHECK(sqe);
|
||||
return sqe;
|
||||
}
|
||||
|
||||
void URing::ProcessCQE(io_uring_cqe *cqe) {
|
||||
Entry *entry = reinterpret_cast<Entry *>(io_uring_cqe_get_data(cqe));
|
||||
PutEntry(entry, cqe->res);
|
||||
io_uring_cqe_seen(&ring_, cqe);
|
||||
}
|
||||
|
||||
} // namespace uring
|
||||
64
uring.h
Normal file
64
uring.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <liburing.h>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace uring {
|
||||
|
||||
// Not thread safe. Instantiate one per thread instead.
|
||||
class URing {
|
||||
public:
|
||||
URing(uint32_t num_entries);
|
||||
~URing();
|
||||
|
||||
// TODO: write/writev don't work for files because they always use offset 0
|
||||
void write(int fd, const void *buf, size_t count,
|
||||
const std::function<void(int32_t res)> &callback);
|
||||
void writev(int fd, const std::vector<iovec> &vecs,
|
||||
const std::function<void(int32_t res)> &callback);
|
||||
void pwritev(int fd, const std::vector<iovec> &vecs, off_t offset,
|
||||
const std::function<void(int32_t res)> &callback);
|
||||
|
||||
// TODO: read/readv don't work for files because they always use offset 0
|
||||
void read(int fd, const void *buf, size_t count,
|
||||
const std::function<void(int32_t res)> &callback);
|
||||
void readv(int fd, const std::vector<iovec> &vecs,
|
||||
const std::function<void(int32_t res)> &callback);
|
||||
void preadv(int fd, const std::vector<iovec> &vecs, off_t offset,
|
||||
const std::function<void(int32_t res)> &callback);
|
||||
|
||||
// Submit all operations queued since the last Submit(). They must all be in
|
||||
// a valid state when this is called.
|
||||
void Submit();
|
||||
|
||||
// Wait for one operation to complete and synchronously call its callback.
|
||||
void Wait();
|
||||
|
||||
// If an operation is complete, synchronously call its callback and return
|
||||
// true.
|
||||
bool Try();
|
||||
|
||||
private:
|
||||
io_uring ring_;
|
||||
int ring_fd_;
|
||||
|
||||
struct Entry {
|
||||
Entry *free_next;
|
||||
std::vector<iovec> vecs;
|
||||
std::function<void(int32_t)> callback;
|
||||
};
|
||||
|
||||
std::vector<Entry> entries_;
|
||||
Entry *free_head_ = nullptr;
|
||||
|
||||
Entry *GetEntry(const std::function<void(int32_t res)> &callback);
|
||||
void PutEntry(Entry *entry, int32_t res);
|
||||
|
||||
io_uring_sqe *GetSQE();
|
||||
|
||||
void ProcessCQE(io_uring_cqe *cqe);
|
||||
};
|
||||
|
||||
} // namespace uring
|
||||
Reference in New Issue
Block a user