Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7112df465 | ||
|
|
ed021c88b9 | ||
|
|
5d360ed401 | ||
|
|
8164c93d5a | ||
|
|
82621702cf |
No files matched your search
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
** Log Files **
|
||||
These should be KeyDB logs, not syslogs or logs from your container manager. If you are reporting a crash there will be a line in your log stating:
|
||||
"=== KEYDB BUG REPORT START: Cut & paste starting from here ==="
|
||||
|
||||
Please copy everything after this line.
|
||||
|
||||
**To Reproduce**
|
||||
Do you know how to reproduce this? If so please provide repro steps.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -1,55 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
test-ubuntu-latest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make BUILD_TLS=yes -j2
|
||||
- name: gen-cert
|
||||
run: ./utils/gen-test-certs.sh
|
||||
- name: test-tls
|
||||
run: |
|
||||
sudo apt-get -y install tcl8.5 tcl-tls
|
||||
./runtest --clients 2 --verbose --tls
|
||||
- name: cluster-test
|
||||
run: |
|
||||
./runtest-cluster --tls
|
||||
- name: sentinel test
|
||||
run: |
|
||||
./runtest-sentinel
|
||||
- name: module tests
|
||||
run: |
|
||||
./runtest-moduleapi
|
||||
|
||||
build-ubuntu-old:
|
||||
runs-on: ubuntu-16.04
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make -j2
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j2
|
||||
|
||||
build-macos-latest:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: make -j2
|
||||
|
||||
build-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make MALLOC=libc -j2
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
name: Daily
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 7 * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
test-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 1200
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.5
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
|
||||
test-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 1200
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make MALLOC=libc
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.5
|
||||
./runtest --accurate --verbose
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make BUILD_TLS=yes -j2
|
||||
- name: "test (tls)"
|
||||
run: |
|
||||
sudo apt-get install tcl8.5 tcl-tls
|
||||
./utils/gen-test-certs.sh
|
||||
./runtest --accurate --verbose --tls
|
||||
- name: test
|
||||
run: ./runtest --accurate --verbose
|
||||
- name: module api test (tls)
|
||||
run: ./runtest-moduleapi --verbose --tls
|
||||
|
||||
test-ubuntu-arm:
|
||||
runs-on: [self-hosted, linux, arm]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j4
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get -y install tcl8.5
|
||||
./runtest --clients 2 --verbose
|
||||
- name: module tests
|
||||
run: |
|
||||
./runtest-moduleapi
|
||||
|
||||
test-valgrind:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make valgrind
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.5 valgrind -y
|
||||
./runtest --valgrind --verbose --clients 1
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --verbose --clients 1
|
||||
@@ -1,22 +0,0 @@
|
||||
name: Endurance
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 * * * *'
|
||||
|
||||
jobs:
|
||||
|
||||
test-endurance:
|
||||
runs-on: octocore
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: make
|
||||
run: |
|
||||
sudo apt-get -y install uuid-dev libcurl4-openssl-dev
|
||||
make -j8
|
||||
- name: test-multithread (5X)
|
||||
run: |
|
||||
sudo apt-get install -y tcl8.5
|
||||
./runtest --loopn 5 --config server-threads 3 --clients 5
|
||||
|
||||
+1
-6
@@ -1,9 +1,6 @@
|
||||
.*.swp
|
||||
core
|
||||
*.o
|
||||
*.xo
|
||||
*.so
|
||||
*.d
|
||||
*.log
|
||||
dump.rdb
|
||||
redis-benchmark
|
||||
@@ -30,7 +27,7 @@ release.h
|
||||
src/transfer.sh
|
||||
src/configs
|
||||
redis.ds
|
||||
src/keydb.conf
|
||||
src/redis.conf
|
||||
src/nodes.conf
|
||||
deps/lua/src/lua
|
||||
deps/lua/src/luac
|
||||
@@ -39,5 +36,3 @@ deps/lua/src/liblua.a
|
||||
.prerequisites
|
||||
*.dSYM
|
||||
Makefile.dep
|
||||
.vscode/*
|
||||
.idea/*
|
||||
@@ -1,3 +0,0 @@
|
||||
[submodule "deps/depot_tools"]
|
||||
path = deps/depot_tools
|
||||
url = https://chromium.googlesource.com/chromium/tools/depot_tools.git
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
language: generic
|
||||
os: osx
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
script: make
|
||||
env: COMPILER_NAME=gcc CXX=g++-5 CC=gcc-5
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- g++-5
|
||||
- nasm
|
||||
sources: &sources
|
||||
- llvm-toolchain-precise-3.8
|
||||
- ubuntu-toolchain-r-test
|
||||
- os: linux
|
||||
script: make MALLOC=libc
|
||||
env: COMPILER_NAME=clang CXX=clang++-3.8 CC=clang-3.8 CXXFLAGS="-stdlib=libc++" LDFLAGS="-stdlib=libc++"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-3.8
|
||||
- libc++-dev
|
||||
- libc++abi-dev
|
||||
- nasm
|
||||
sources: *sources
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"zmalloc.h": "c",
|
||||
"stat.h": "c",
|
||||
"array": "cpp",
|
||||
"atomic": "cpp",
|
||||
"*.tcc": "cpp",
|
||||
"cctype": "cpp",
|
||||
"chrono": "cpp",
|
||||
"clocale": "cpp",
|
||||
"cmath": "cpp",
|
||||
"condition_variable": "cpp",
|
||||
"cstdarg": "cpp",
|
||||
"cstddef": "cpp",
|
||||
"cstdint": "cpp",
|
||||
"cstdio": "cpp",
|
||||
"cstdlib": "cpp",
|
||||
"cstring": "cpp",
|
||||
"ctime": "cpp",
|
||||
"cwchar": "cpp",
|
||||
"cwctype": "cpp",
|
||||
"deque": "cpp",
|
||||
"list": "cpp",
|
||||
"unordered_map": "cpp",
|
||||
"vector": "cpp",
|
||||
"exception": "cpp",
|
||||
"fstream": "cpp",
|
||||
"functional": "cpp",
|
||||
"future": "cpp",
|
||||
"initializer_list": "cpp",
|
||||
"iomanip": "cpp",
|
||||
"iosfwd": "cpp",
|
||||
"iostream": "cpp",
|
||||
"istream": "cpp",
|
||||
"limits": "cpp",
|
||||
"memory": "cpp",
|
||||
"mutex": "cpp",
|
||||
"new": "cpp",
|
||||
"numeric": "cpp",
|
||||
"optional": "cpp",
|
||||
"ostream": "cpp",
|
||||
"ratio": "cpp",
|
||||
"scoped_allocator": "cpp",
|
||||
"sstream": "cpp",
|
||||
"stdexcept": "cpp",
|
||||
"streambuf": "cpp",
|
||||
"string_view": "cpp",
|
||||
"system_error": "cpp",
|
||||
"thread": "cpp",
|
||||
"cinttypes": "cpp",
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"typeinfo": "cpp",
|
||||
"utility": "cpp"
|
||||
}
|
||||
}
|
||||
+11
-2072
File diff suppressed because it is too large.
Load diff
+5
-15
@@ -14,7 +14,9 @@ each source file that you contribute.
|
||||
|
||||
PLEASE DO NOT POST GENERAL QUESTIONS that are not about bugs or suspected
|
||||
bugs in the Github issues system. We'll be very happy to help you and provide
|
||||
all the support in the mailing list.
|
||||
all the support at the Reddit sub:
|
||||
|
||||
http://reddit.com/r/redis
|
||||
|
||||
There is also an active community of Redis users at Stack Overflow:
|
||||
|
||||
@@ -22,12 +24,7 @@ each source file that you contribute.
|
||||
|
||||
# How to provide a patch for a new feature
|
||||
|
||||
1. If it is a major feature or a semantical change, please don't start coding
|
||||
straight away: if your feature is not a conceptual fit you'll lose a lot of
|
||||
time writing the code without any reason. Start by posting in the mailing list
|
||||
and creating an issue at Github with the description of, exactly, what you want
|
||||
to accomplish and why. Use cases are important for features to be accepted.
|
||||
Here you'll see if there is consensus about your idea.
|
||||
1. If it is a major feature or a semantical change, please post it as a new submission in r/redis on Reddit at http://reddit.com/r/redis. Try to be passionate about why the feature is needed, make users upvote your proposal to gain traction and so forth. Read feedbacks about the community. But in this first step **please don't write code yet**.
|
||||
|
||||
2. If in step 1 you get an acknowledgment from the project leaders, use the
|
||||
following procedure to submit a patch:
|
||||
@@ -38,13 +35,6 @@ Here you'll see if there is consensus about your idea.
|
||||
d. Initiate a pull request on github ( https://help.github.com/articles/creating-a-pull-request/ )
|
||||
e. Done :)
|
||||
|
||||
3. Keep in mind that we are very overloaded, so issues and PRs sometimes wait
|
||||
for a *very* long time. However this is not lack of interest, as the project
|
||||
gets more and more users, we find ourselves in a constant need to prioritize
|
||||
certain issues/PRs over others. If you think your issue/PR is very important
|
||||
try to popularize it, have other users commenting and sharing their point of
|
||||
view and so forth. This helps.
|
||||
|
||||
4. For minor fixes just open a pull request on Github.
|
||||
For minor fixes just open a pull request on Github.
|
||||
|
||||
Thanks!
|
||||
+2
-2
@@ -4,7 +4,7 @@ FROM ubuntu:18.04
|
||||
|
||||
RUN apt-get update \
|
||||
&& DEBIAN_FRONTEND=noninteractive apt-get install -qqy \
|
||||
build-essential nasm autotools-dev autoconf libcurl4-openssl-dev libjemalloc-dev tcl tcl-dev uuid-dev libcurl4-openssl-dev \
|
||||
build-essential nasm autotools-dev autoconf libjemalloc-dev tcl tcl-dev \
|
||||
&& apt-get clean
|
||||
|
||||
CMD make
|
||||
CMD make
|
||||
@@ -1,55 +1,36 @@
|
||||

|
||||

|
||||
[](https://gitter.im/KeyDB/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://stackshare.io/eq-alpha-technology-inc/eq-alpha-technology-inc)
|
||||
|
||||
##### New! Want to extend KeyDB with Javascript? Try [ModJS](https://github.com/JohnSully/ModJS)
|
||||
|
||||
##### Need Help? Check out our extensive [documentation](https://docs.keydb.dev).
|
||||
|
||||
##### Have feedback? Take our quick survey: https://www.surveymonkey.com/r/Y9XNS93
|
||||
[](https://travis-ci.org/JohnSully/KeyDB) [](https://gitter.im/KeyDB/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
|
||||
What is KeyDB?
|
||||
--------------
|
||||
|
||||
KeyDB is a high performance fork of Redis with a focus on multithreading, memory efficiency, and high throughput. In addition to multithreading, KeyDB also has features only available in Redis Enterprise such as [Active Replication](https://github.com/JohnSully/KeyDB/wiki/Active-Replication), [FLASH storage](https://github.com/JohnSully/KeyDB/wiki/FLASH-Storage) support, and some not available at all such as direct backup to AWS S3.
|
||||
KeyDB is a high performance fork of Redis focusing on multithreading, memory efficiency, and high throughput. In addition to multithreading KeyDB also has features only available in Redis Enterprise such as FLASH storage support, and some not available at all such as direct backup to AWS S3.
|
||||
|
||||
KeyDB maintains full compatibility with the Redis protocol, modules, and scripts. This includes the atomicity guarantees for scripts and transactions. Because KeyDB keeps in sync with Redis development KeyDB is a superset of Redis functionality, making KeyDB a drop in replacement for existing Redis deployments.
|
||||
On the same hardware KeyDB can perform twice as many queries per second as Redis, with 60% lower latency.
|
||||
|
||||
On the same hardware KeyDB can perform twice as many queries per second as Redis, with 60% lower latency. Active-Replication simplifies hot-spare failover allowing you to easily distribute writes over replicas and use simple TCP based load balancing/failover. KeyDB's higher performance allows you to do more on less hardware which reduces operation costs and complexity.
|
||||
|
||||
<img src="https://keydb.dev/assets/img/blog/5x_opspersecVSdatasize.PNG"/>
|
||||
|
||||
See the full benchmark results and setup information here: https://docs.keydb.dev/blog/2019/10/07/blog-post/
|
||||
|
||||
Why fork Redis?
|
||||
---------------
|
||||
|
||||
KeyDB has a different philosophy on how the codebase should evolve. We feel that ease of use, high performance, and a "batteries included" approach is the best way to create a good user experience. While we have great respect for the Redis maintainers it is our opinion that the Redis approach focuses too much on simplicity of the code base at the expense of complexity for the user. This results in the need for external components and workarounds to solve common problems - resulting in more complexity overall.
|
||||
|
||||
Because of this difference of opinion features which are right for KeyDB may not be appropriate for Redis. A fork allows us to explore this new development path and implement features which may never be a part of Redis. KeyDB keeps in sync with upstream Redis changes, and where applicable we upstream bug fixes and changes. It is our hope that the two projects can continue to grow and learn from each other.
|
||||
|
||||
Additional Resources
|
||||
--------------------
|
||||
KeyDB has full compatibility with the Redis protocol, modules, and scripts. This includes full support for transactions, and atomic execution of scripts. For more information see our architecture section below.
|
||||
|
||||
Try our docker container: https://hub.docker.com/r/eqalpha/keydb
|
||||
|
||||
Talk on Gitter: https://gitter.im/KeyDB
|
||||
|
||||
Visit our Website: https://keydb.dev
|
||||
New: Active Replica Support
|
||||
---------------------------
|
||||
|
||||
See options for channel partners and support contracts: https://keydb.dev/support.html
|
||||
New! KeyDB now has support for Active Replicas. This feature greatly simplifies hot-spare failover and allows you to distribute writes over replicas instead of just a single master. For more information [see the wiki page](https://github.com/JohnSully/KeyDB/wiki/KeyDB-(Redis-Fork):-Active-Replica-Support).
|
||||
|
||||
Learn with KeyDB’s official documentation site: https://docs.keydb.dev
|
||||
Why fork Redis?
|
||||
---------------
|
||||
|
||||
[Subscribe to the KeyDB mailing list](https://eqalpha.us20.list-manage.com/subscribe/post?u=978f486c2f95589b24591a9cc&id=4ab9220500)
|
||||
|
||||
Management GUI: We recommend [FastoNoSQL](https://fastonosql.com/) which has official KeyDB support.
|
||||
The Redis maintainers have continually reiterated that they do not plan to support multithreading. While we have great respect for the redis team, we feel the analysis justifying this decision is incorrect. In addition we wanted open source implementations of features currently only available in proprietary modules. We feel a fork is the best way to accelerate development in the areas of most interest to us.
|
||||
|
||||
We plan to track the Redis repo closely and hope our projects can learn from each other.
|
||||
|
||||
Benchmarking KeyDB
|
||||
------------------
|
||||
|
||||
<img src=https://cdn-images-1.medium.com/max/1400/1*s7mTb7Qb0kxc951mz8bdgA.png width=420 height=300/><img src=https://cdn-images-1.medium.com/max/1400/1*R00A5U4AFGohGOYHMfT6fA.png height=300/>
|
||||
|
||||
Please note keydb-benchmark and redis-benchmark are currently single threaded and too slow to properly benchmark KeyDB. We recommend using a redis cluster benchmark tool such as [memtier](https://github.com/RedisLabs/memtier_benchmark). Please ensure your machine has enough cores for both KeyDB and memteir if testing locally. KeyDB expects exclusive use of any cores assigned to it.
|
||||
|
||||
For more details on how we benchmarked KeyDB along with performance numbers check out our blog post: [Redis Should Be Multithreaded](https://medium.com/@john_63123/redis-should-be-multi-threaded-e28319cab744?source=friends_link&sk=7ce8e9fe3ec8224a4d27ef075d085457)
|
||||
@@ -66,50 +47,27 @@ The number of threads used to serve requests. This should be related to the num
|
||||
|
||||
scratch-file-path /path
|
||||
|
||||
If you would like to use the [FLASH backed](https://github.com/JohnSully/KeyDB/wiki/FLASH-Storage) storage this option configures the directory for KeyDB's temporary files. This feature relies on snapshotting to work so must be used on a BTRFS filesystem. ZFS may also work but is untested. With this feature KeyDB will use RAM as a cache and page to disk as necessary. NOTE: This requires special compilation options, see Building KeyDB below.
|
||||
If you would like to use the FLASH backed storage this option configures the directory for KeyDB's temporary files. This feature relies on snapshotting to work so must be used on a BTRFS filesystem. ZFS may also work but is untested. With this feature KeyDB will use RAM as a cache and page to disk as necessary. NOTE: This requires special compilation options, see Building KeyDB below.
|
||||
|
||||
db-s3-object /path/to/bucket
|
||||
|
||||
If you would like KeyDB to dump and load directly to AWS S3 this option specifies the bucket. Using this option with the traditional RDB options will result in KeyDB backing up twice to both locations. If both are specified KeyDB will first attempt to load from the local dump file and if that fails load from S3. This requires the AWS CLI tools to be installed and configured which are used under the hood to transfer the data.
|
||||
|
||||
active-replica yes
|
||||
|
||||
If you are using active-active replication set `active-replica` option to “yes”. This will enable both instances to accept reads and writes while remaining synced. [Click here](https://docs.keydb.dev/docs/active-rep/) to see more on active-rep in our docs section. There are also [docker examples]( https://docs.keydb.dev/docs/docker-active-rep/) on docs.
|
||||
|
||||
All other configuration options behave as you'd expect. Your existing configuration files should continue to work unchanged.
|
||||
|
||||
Building KeyDB
|
||||
--------------
|
||||
|
||||
KeyDB can be compiled and is tested for use on Linux. KeyDB currently relies on SO_REUSEPORT's load balancing behavior which is available only in Linux. When we support marshalling connections across threads we plan to support other operating systems such as FreeBSD.
|
||||
|
||||
Install dependencies:
|
||||
|
||||
% sudo apt install build-essential nasm autotools-dev autoconf libjemalloc-dev tcl tcl-dev uuid-dev libcurl4-openssl-dev
|
||||
KeyDB can be compiled and is tested for use on Linux. KeyDB currently relies on SO_REUSEADDR's load balancing behavior which is available only in Linux. When we support marshalling connections across threads we plan to support other operating systems such as FreeBSD.
|
||||
|
||||
Compiling is as simple as:
|
||||
|
||||
% make
|
||||
|
||||
To build with TLS support, you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu) and run:
|
||||
|
||||
% make BUILD_TLS=yes
|
||||
|
||||
You can enable flash support with:
|
||||
You can enable flash support with (Note: autoconf and autotools must be installed):
|
||||
|
||||
% make MALLOC=memkind
|
||||
|
||||
***Note that the following dependencies may be needed:
|
||||
% sudo apt-get install autoconf autotools-dev libnuma-dev libtool
|
||||
|
||||
If TLS is built, running the tests with TLS enabled (you will need `tcl-tls`
|
||||
installed):
|
||||
|
||||
% ./utils/gen-test-certs.sh
|
||||
% ./runtest --tls
|
||||
|
||||
|
||||
Fixing build problems with dependencies or cached build options
|
||||
---------
|
||||
|
||||
@@ -192,14 +150,6 @@ as options using the command line. Examples:
|
||||
All the options in keydb.conf are also supported as options using the command
|
||||
line, with exactly the same name.
|
||||
|
||||
|
||||
Running Redis with TLS:
|
||||
------------------
|
||||
|
||||
Please consult the [TLS.md](TLS.md) file for more information on
|
||||
how to use Redis with TLS.
|
||||
|
||||
|
||||
Playing with KeyDB
|
||||
------------------
|
||||
|
||||
@@ -220,7 +170,7 @@ then in another terminal try the following:
|
||||
(integer) 2
|
||||
keydb>
|
||||
|
||||
You can find the list of all the available commands at https://docs.keydb.dev/docs/commands/
|
||||
You can find the list of all the available commands at http://redis.io/commands.
|
||||
|
||||
Installing KeyDB
|
||||
-----------------
|
||||
@@ -263,18 +213,23 @@ Future work:
|
||||
|
||||
Docker Build
|
||||
------------
|
||||
Build the latest binaries from the github unstable branch within a docker container. Note this is built for Ubuntu 18.04.
|
||||
Simply make a directory you would like to have the latest binaries dumped in, then run the following commmand with your updated path:
|
||||
```
|
||||
$ docker run -it --rm -v /path-to-dump-binaries:/keydb_bin eqalpha/keydb-build-bin
|
||||
```
|
||||
You should receive the following files: keydb-benchmark, keydb-check-aof, keydb-check-rdb, keydb-cli, keydb-sentinel, keydb-server
|
||||
|
||||
If you are looking to enable flash support with the build (make MALLOC=memkind) then use the following command:
|
||||
Run the following commands for a full source download and build:
|
||||
|
||||
```
|
||||
$ docker run -it --rm -v /path-to-dump-binaries:/keydb_bin eqalpha/keydb-build-bin:flash
|
||||
git clone git@github.com:JohnSully/KeyDB.git
|
||||
docker run -it --rm `pwd`/KeyDB:/build -w /build devopsdood/keydb-builder make
|
||||
```
|
||||
Please note that you will need libcurl4-openssl-dev in order to run keydb. With flash version you may need libnuma-dev and libtool installed in order to run the binaries. Keep this in mind especially when running in a container. For a copy of all our Dockerfiles, please see them on [docs]( https://docs.keydb.dev/docs/dockerfiles/).
|
||||
|
||||
Then you have fresh binaries built, you can also pass any other options to the make command above after the word make. E.g.
|
||||
|
||||
```docker run -it --rm `pwd`/KeyDB:/build -w /build devopsdood/keydb-builder make MAllOC=memkind```
|
||||
|
||||
The above commands will build you binaries in the src directory. Standard `make install` without Docker command will work after if you wish to install
|
||||
|
||||
If you'd prefer you can build the Dockerfile in the repo instead of pulling the above container for use:
|
||||
|
||||
`docker build -t KeyDB .`
|
||||
|
||||
Code contributions
|
||||
-----------------
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
TLS Support
|
||||
===========
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
### Building
|
||||
|
||||
To build with TLS support you'll need OpenSSL development libraries (e.g.
|
||||
libssl-dev on Debian/Ubuntu).
|
||||
|
||||
Run `make BUILD_TLS=yes`.
|
||||
|
||||
### Tests
|
||||
|
||||
To run Redis test suite with TLS, you'll need TLS support for TCL (i.e.
|
||||
`tcl-tls` package on Debian/Ubuntu).
|
||||
|
||||
1. Run `./utils/gen-test-certs.sh` to generate a root CA and a server
|
||||
certificate.
|
||||
|
||||
2. Run `./runtest --tls` or `./runtest-cluster --tls` to run Redis and Redis
|
||||
Cluster tests in TLS mode.
|
||||
|
||||
### Running manually
|
||||
|
||||
To manually run a Redis server with TLS mode (assuming `gen-test-certs.sh` was
|
||||
invoked so sample certificates/keys are available):
|
||||
|
||||
./src/redis-server --tls-port 6379 --port 0 \
|
||||
--tls-cert-file ./tests/tls/redis.crt \
|
||||
--tls-key-file ./tests/tls/redis.key \
|
||||
--tls-ca-cert-file ./tests/tls/ca.crt
|
||||
|
||||
To connect to this Redis server with `redis-cli`:
|
||||
|
||||
./src/redis-cli --tls \
|
||||
--cert ./tests/tls/redis.crt \
|
||||
--key ./tests/tls/redis.key \
|
||||
--cacert ./tests/tls/ca.crt
|
||||
|
||||
This will disable TCP and enable TLS on port 6379. It's also possible to have
|
||||
both TCP and TLS available, but you'll need to assign different ports.
|
||||
|
||||
To make a Replica connect to the master using TLS, use `--tls-replication yes`,
|
||||
and to make Redis Cluster use TLS across nodes use `--tls-cluster yes`.
|
||||
|
||||
Connections
|
||||
-----------
|
||||
|
||||
All socket operations now go through a connection abstraction layer that hides
|
||||
I/O and read/write event handling from the caller.
|
||||
|
||||
Note that unlike Redis, KeyDB fully supports multithreading of TLS connections.
|
||||
|
||||
To-Do List
|
||||
----------
|
||||
|
||||
- [ ] Add session caching support. Check if/how it's handled by clients to
|
||||
assess how useful/important it is.
|
||||
- [ ] redis-benchmark support. The current implementation is a mix of using
|
||||
hiredis for parsing and basic networking (establishing connections), but
|
||||
directly manipulating sockets for most actions. This will need to be cleaned
|
||||
up for proper TLS support. The best approach is probably to migrate to hiredis
|
||||
async mode.
|
||||
- [ ] redis-cli `--slave` and `--rdb` support.
|
||||
|
||||
Multi-port
|
||||
----------
|
||||
|
||||
Consider the implications of allowing TLS to be configured on a separate port,
|
||||
making Redis listening on multiple ports:
|
||||
|
||||
1. Startup banner port notification
|
||||
2. Proctitle
|
||||
3. How slaves announce themselves
|
||||
4. Cluster bus port calculation
|
||||
Vendored
+1
-5
@@ -41,13 +41,9 @@ distclean:
|
||||
|
||||
.PHONY: distclean
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
HIREDIS_MAKE_FLAGS = USE_SSL=1
|
||||
endif
|
||||
|
||||
hiredis: .make-prerequisites
|
||||
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
|
||||
cd hiredis && $(MAKE) static $(HIREDIS_MAKE_FLAGS)
|
||||
cd hiredis && $(MAKE) static
|
||||
|
||||
.PHONY: hiredis
|
||||
|
||||
|
||||
@@ -5,4 +5,3 @@
|
||||
/*.dylib
|
||||
/*.a
|
||||
/*.pc
|
||||
*.dSYM
|
||||
Vendored
+11
-63
@@ -26,72 +26,20 @@ addons:
|
||||
- libc6-dev-i386
|
||||
- libc6-dbg:i386
|
||||
- gcc-multilib
|
||||
- g++-multilib
|
||||
- valgrind
|
||||
|
||||
env:
|
||||
- BITS="32"
|
||||
- BITS="64"
|
||||
|
||||
script:
|
||||
- EXTRA_CMAKE_OPTS="-DENABLE_EXAMPLES:BOOL=ON -DHIREDIS_SSL:BOOL=ON";
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
if [ "$BITS" == "32" ]; then
|
||||
CFLAGS="-m32 -Werror";
|
||||
CXXFLAGS="-m32 -Werror";
|
||||
LDFLAGS="-m32";
|
||||
EXTRA_CMAKE_OPTS=;
|
||||
else
|
||||
CFLAGS="-Werror";
|
||||
CXXFLAGS="-Werror";
|
||||
fi;
|
||||
else
|
||||
TEST_PREFIX="valgrind --track-origins=yes --leak-check=full";
|
||||
if [ "$BITS" == "32" ]; then
|
||||
CFLAGS="-m32 -Werror";
|
||||
CXXFLAGS="-m32 -Werror";
|
||||
LDFLAGS="-m32";
|
||||
EXTRA_CMAKE_OPTS=;
|
||||
else
|
||||
CFLAGS="-Werror";
|
||||
CXXFLAGS="-Werror";
|
||||
fi;
|
||||
fi;
|
||||
export CFLAGS CXXFLAGS LDFLAGS TEST_PREFIX EXTRA_CMAKE_OPTS
|
||||
- mkdir build/ && cd build/
|
||||
- cmake .. ${EXTRA_CMAKE_OPTS}
|
||||
- make VERBOSE=1
|
||||
- ctest -V
|
||||
- CFLAGS="-Werror"
|
||||
- PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" CFLAGS="-Werror"
|
||||
- TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# Windows MinGW cross compile on Linux
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: mingw
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- ninja-build
|
||||
- gcc-mingw-w64-x86-64
|
||||
- g++-mingw-w64-x86-64
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_WITH_INSTALL_RPATH=on
|
||||
- ninja -v
|
||||
exclude:
|
||||
- os: osx
|
||||
env: PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
|
||||
# Windows MSVC 2017
|
||||
- os: windows
|
||||
compiler: msvc
|
||||
env:
|
||||
- MATRIX_EVAL="CC=cl.exe && CXX=cl.exe"
|
||||
before_install:
|
||||
- eval "${MATRIX_EVAL}"
|
||||
install:
|
||||
- choco install ninja
|
||||
script:
|
||||
- mkdir build && cd build
|
||||
- cmd.exe /C '"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 &&
|
||||
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release &&
|
||||
ninja -v'
|
||||
- ctest -V
|
||||
- os: osx
|
||||
env: TARGET="32bit" TARGET_VARS="32bit-vars" PRE="valgrind --track-origins=yes --leak-check=full"
|
||||
|
||||
script: make $TARGET CFLAGS="$CFLAGS" && make check PRE="$PRE" && make $TARGET_VARS hiredis-example
|
||||
Vendored
+2
-13
@@ -12,16 +12,6 @@
|
||||
compare to other values, casting might be necessary or can be removed, if
|
||||
casting was applied before.
|
||||
|
||||
### 0.x.x (unreleased)
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Change `redisReply.len` to `size_t`, as it denotes the the size of a string
|
||||
|
||||
User code should compare this to `size_t` values as well.
|
||||
If it was used to compare to other values, casting might be necessary or can be removed, if casting was applied before.
|
||||
|
||||
* `redisReplyObjectFunctions.createArray` now takes `size_t` for its length parameter.
|
||||
|
||||
### 0.14.0 (2018-09-25)
|
||||
|
||||
* Make string2ll static to fix conflict with Redis (Tom Lee [c3188b])
|
||||
@@ -60,9 +50,8 @@ If it was used to compare to other values, casting might be necessary or can be
|
||||
* Import latest upstream sds. This breaks applications that are linked against the old hiredis v0.13
|
||||
* Fix warnings, when compiled with -Wshadow
|
||||
* Make hiredis compile in Cygwin on Windows, now CI-tested
|
||||
* Bulk and multi-bulk lengths less than -1 or greater than `LLONG_MAX` are now
|
||||
protocol errors. This is consistent with the RESP specification. On 32-bit
|
||||
platforms, the upper bound is lowered to `SIZE_MAX`.
|
||||
|
||||
**BREAKING CHANGES**:
|
||||
|
||||
* Remove backwards compatibility macro's
|
||||
|
||||
|
||||
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0)
|
||||
INCLUDE(GNUInstallDirs)
|
||||
PROJECT(hiredis)
|
||||
|
||||
OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF)
|
||||
|
||||
MACRO(getVersionBit name)
|
||||
SET(VERSION_REGEX "^#define ${name} (.+)$")
|
||||
FILE(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/hiredis.h"
|
||||
VERSION_BIT REGEX ${VERSION_REGEX})
|
||||
STRING(REGEX REPLACE ${VERSION_REGEX} "\\1" ${name} "${VERSION_BIT}")
|
||||
ENDMACRO(getVersionBit)
|
||||
|
||||
getVersionBit(HIREDIS_MAJOR)
|
||||
getVersionBit(HIREDIS_MINOR)
|
||||
getVersionBit(HIREDIS_PATCH)
|
||||
getVersionBit(HIREDIS_SONAME)
|
||||
SET(VERSION "${HIREDIS_MAJOR}.${HIREDIS_MINOR}.${HIREDIS_PATCH}")
|
||||
MESSAGE("Detected version: ${VERSION}")
|
||||
|
||||
PROJECT(hiredis VERSION "${VERSION}")
|
||||
|
||||
SET(ENABLE_EXAMPLES OFF CACHE BOOL "Enable building hiredis examples")
|
||||
|
||||
ADD_LIBRARY(hiredis SHARED
|
||||
async.c
|
||||
dict.c
|
||||
hiredis.c
|
||||
net.c
|
||||
read.c
|
||||
sds.c
|
||||
sockcompat.c)
|
||||
|
||||
SET_TARGET_PROPERTIES(hiredis
|
||||
PROPERTIES
|
||||
VERSION "${HIREDIS_SONAME}")
|
||||
IF(WIN32 OR MINGW)
|
||||
TARGET_LINK_LIBRARIES(hiredis PRIVATE ws2_32)
|
||||
ENDIF()
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis PUBLIC .)
|
||||
|
||||
CONFIGURE_FILE(hiredis.pc.in hiredis.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
INSTALL(FILES hiredis.h read.h sds.h async.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(DIRECTORY adapters
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
|
||||
IF(ENABLE_SSL)
|
||||
IF (NOT OPENSSL_ROOT_DIR)
|
||||
IF (APPLE)
|
||||
SET(OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
FIND_PACKAGE(OpenSSL REQUIRED)
|
||||
ADD_LIBRARY(hiredis_ssl SHARED
|
||||
ssl.c)
|
||||
TARGET_INCLUDE_DIRECTORIES(hiredis_ssl PRIVATE "${OPENSSL_INCLUDE_DIR}")
|
||||
TARGET_LINK_LIBRARIES(hiredis_ssl PRIVATE ${OPENSSL_LIBRARIES})
|
||||
CONFIGURE_FILE(hiredis_ssl.pc.in hiredis_ssl.pc @ONLY)
|
||||
|
||||
INSTALL(TARGETS hiredis_ssl
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}")
|
||||
|
||||
INSTALL(FILES hiredis_ssl.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/hiredis)
|
||||
|
||||
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/hiredis_ssl.pc
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT (WIN32 OR MINGW))
|
||||
ENABLE_TESTING()
|
||||
ADD_EXECUTABLE(hiredis-test test.c)
|
||||
TARGET_LINK_LIBRARIES(hiredis-test hiredis)
|
||||
ADD_TEST(NAME hiredis-test
|
||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/test.sh)
|
||||
ENDIF()
|
||||
|
||||
# Add examples
|
||||
IF(ENABLE_EXAMPLES)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
ENDIF(ENABLE_EXAMPLES)
|
||||
Vendored
+23
-83
@@ -3,17 +3,11 @@
|
||||
# Copyright (C) 2010-2011 Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
# This file is released under the BSD license, see the COPYING file
|
||||
|
||||
OBJ=net.o hiredis.o sds.o async.o read.o sockcompat.o
|
||||
SSL_OBJ=ssl.o
|
||||
OBJ=net.o hiredis.o sds.o async.o read.o
|
||||
EXAMPLES=hiredis-example hiredis-example-libevent hiredis-example-libev hiredis-example-glib
|
||||
ifeq ($(USE_SSL),1)
|
||||
EXAMPLES+=hiredis-example-ssl hiredis-example-libevent-ssl
|
||||
endif
|
||||
TESTS=hiredis-test
|
||||
LIBNAME=libhiredis
|
||||
SSL_LIBNAME=libhiredis_ssl
|
||||
PKGCONFNAME=hiredis.pc
|
||||
SSL_PKGCONFNAME=hiredis_ssl.pc
|
||||
|
||||
HIREDIS_MAJOR=$(shell grep HIREDIS_MAJOR hiredis.h | awk '{print $$3}')
|
||||
HIREDIS_MINOR=$(shell grep HIREDIS_MINOR hiredis.h | awk '{print $$3}')
|
||||
@@ -45,7 +39,7 @@ export REDIS_TEST_CONFIG
|
||||
CC:=$(shell sh -c 'type $${CC%% *} >/dev/null 2>/dev/null && echo $(CC) || echo gcc')
|
||||
CXX:=$(shell sh -c 'type $${CXX%% *} >/dev/null 2>/dev/null && echo $(CXX) || echo g++')
|
||||
OPTIMIZATION?=-O3
|
||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings -Wno-missing-field-initializers
|
||||
WARNINGS=-Wall -W -Wstrict-prototypes -Wwrite-strings
|
||||
DEBUG_FLAGS?= -g -ggdb
|
||||
REAL_CFLAGS=$(OPTIMIZATION) -fPIC $(CPPFLAGS) $(CFLAGS) $(WARNINGS) $(DEBUG_FLAGS)
|
||||
REAL_LDFLAGS=$(LDFLAGS)
|
||||
@@ -55,30 +49,12 @@ STLIBSUFFIX=a
|
||||
DYLIB_MINOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_SONAME)
|
||||
DYLIB_MAJOR_NAME=$(LIBNAME).$(DYLIBSUFFIX).$(HIREDIS_MAJOR)
|
||||
DYLIBNAME=$(LIBNAME).$(DYLIBSUFFIX)
|
||||
SSL_DYLIBNAME=$(SSL_LIBNAME).$(DYLIBSUFFIX)
|
||||
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME)
|
||||
DYLIB_MAKE_CMD=$(CC) -shared -Wl,-soname,$(DYLIB_MINOR_NAME) -o $(DYLIBNAME) $(LDFLAGS)
|
||||
STLIBNAME=$(LIBNAME).$(STLIBSUFFIX)
|
||||
SSL_STLIBNAME=$(SSL_LIBNAME).$(STLIBSUFFIX)
|
||||
STLIB_MAKE_CMD=$(AR) rcs
|
||||
STLIB_MAKE_CMD=ar rcs $(STLIBNAME)
|
||||
|
||||
# Platform-specific overrides
|
||||
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
|
||||
|
||||
USE_SSL?=0
|
||||
|
||||
# This is required for test.c only
|
||||
ifeq ($(USE_SSL),1)
|
||||
CFLAGS+=-DHIREDIS_TEST_SSL
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),Linux)
|
||||
SSL_LDFLAGS=-lssl -lcrypto
|
||||
else
|
||||
OPENSSL_PREFIX?=/usr/local/opt/openssl
|
||||
CFLAGS+=-I$(OPENSSL_PREFIX)/include
|
||||
SSL_LDFLAGS+=-L$(OPENSSL_PREFIX)/lib -lssl -lcrypto
|
||||
endif
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
REAL_LDFLAGS+= -ldl -lnsl -lsocket
|
||||
DYLIB_MAKE_CMD=$(CC) -G -o $(DYLIBNAME) -h $(DYLIB_MINOR_NAME) $(LDFLAGS)
|
||||
@@ -90,61 +66,40 @@ ifeq ($(uname_S),Darwin)
|
||||
endif
|
||||
|
||||
all: $(DYLIBNAME) $(STLIBNAME) hiredis-test $(PKGCONFNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
all: $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(SSL_PKGCONFNAME)
|
||||
endif
|
||||
|
||||
# Deps (use make dep to generate this)
|
||||
async.o: async.c fmacros.h async.h hiredis.h read.h sds.h net.h dict.c dict.h
|
||||
dict.o: dict.c fmacros.h dict.h
|
||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h win32.h
|
||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h sockcompat.h win32.h
|
||||
hiredis.o: hiredis.c fmacros.h hiredis.h read.h sds.h net.h
|
||||
net.o: net.c fmacros.h net.h hiredis.h read.h sds.h
|
||||
read.o: read.c fmacros.h read.h sds.h
|
||||
sds.o: sds.c sds.h
|
||||
sockcompat.o: sockcompat.c sockcompat.h
|
||||
ssl.o: ssl.c hiredis.h
|
||||
test.o: test.c fmacros.h hiredis.h read.h sds.h
|
||||
|
||||
$(DYLIBNAME): $(OBJ)
|
||||
$(DYLIB_MAKE_CMD) -o $(DYLIBNAME) $(OBJ) $(REAL_LDFLAGS)
|
||||
$(DYLIB_MAKE_CMD) $(OBJ)
|
||||
|
||||
$(STLIBNAME): $(OBJ)
|
||||
$(STLIB_MAKE_CMD) $(STLIBNAME) $(OBJ)
|
||||
|
||||
$(SSL_DYLIBNAME): $(SSL_OBJ)
|
||||
$(DYLIB_MAKE_CMD) -o $(SSL_DYLIBNAME) $(SSL_OBJ) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
|
||||
$(SSL_STLIBNAME): $(SSL_OBJ)
|
||||
$(STLIB_MAKE_CMD) $(SSL_STLIBNAME) $(SSL_OBJ)
|
||||
$(STLIB_MAKE_CMD) $(OBJ)
|
||||
|
||||
dynamic: $(DYLIBNAME)
|
||||
static: $(STLIBNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
dynamic: $(SSL_DYLIBNAME)
|
||||
static: $(SSL_STLIBNAME)
|
||||
endif
|
||||
|
||||
# Binaries:
|
||||
hiredis-example-libevent: examples/example-libevent.c adapters/libevent.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-libevent-ssl: examples/example-libevent-ssl.c adapters/libevent.h $(STLIBNAME) $(SSL_STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -levent $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -levent $(STLIBNAME)
|
||||
|
||||
hiredis-example-libev: examples/example-libev.c adapters/libev.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -lev $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -lev $(STLIBNAME)
|
||||
|
||||
hiredis-example-glib: examples/example-glib.c adapters/glib.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(shell pkg-config --cflags --libs glib-2.0) $(STLIBNAME)
|
||||
|
||||
hiredis-example-ivykis: examples/example-ivykis.c adapters/ivykis.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -livykis $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -livykis $(STLIBNAME)
|
||||
|
||||
hiredis-example-macosx: examples/example-macosx.c adapters/macosx.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
|
||||
hiredis-example-ssl: examples/example-ssl.c $(STLIBNAME) $(SSL_STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(SSL_STLIBNAME) $(REAL_LDFLAGS) $(SSL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< -framework CoreFoundation $(STLIBNAME)
|
||||
|
||||
ifndef AE_DIR
|
||||
hiredis-example-ae:
|
||||
@@ -161,7 +116,7 @@ hiredis-example-libuv:
|
||||
@false
|
||||
else
|
||||
hiredis-example-libuv: examples/example-libuv.c adapters/libuv.h $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. -I$(LIBUV_DIR)/include $< $(LIBUV_DIR)/.libs/libuv.a -lpthread -lrt $(STLIBNAME)
|
||||
endif
|
||||
|
||||
ifeq ($(and $(QT_MOC),$(QT_INCLUDE_DIR),$(QT_LIBRARY_DIR)),)
|
||||
@@ -178,33 +133,32 @@ hiredis-example-qt: examples/example-qt.cpp adapters/qt.h $(STLIBNAME)
|
||||
endif
|
||||
|
||||
hiredis-example: examples/example.c $(STLIBNAME)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) -I. $< $(STLIBNAME) $(REAL_LDFLAGS)
|
||||
$(CC) -o examples/$@ $(REAL_CFLAGS) $(REAL_LDFLAGS) -I. $< $(STLIBNAME)
|
||||
|
||||
examples: $(EXAMPLES)
|
||||
|
||||
TEST_LIBS = $(STLIBNAME)
|
||||
ifeq ($(USE_SSL),1)
|
||||
TEST_LIBS += $(SSL_STLIBNAME) -lssl -lcrypto -lpthread
|
||||
endif
|
||||
hiredis-test: test.o $(TEST_LIBS)
|
||||
hiredis-test: test.o $(STLIBNAME)
|
||||
|
||||
hiredis-%: %.o $(STLIBNAME)
|
||||
$(CC) $(REAL_CFLAGS) -o $@ $< $(TEST_LIBS) $(REAL_LDFLAGS)
|
||||
$(CC) $(REAL_CFLAGS) -o $@ $(REAL_LDFLAGS) $< $(STLIBNAME)
|
||||
|
||||
test: hiredis-test
|
||||
./hiredis-test
|
||||
|
||||
check: hiredis-test
|
||||
TEST_SSL=$(USE_SSL) ./test.sh
|
||||
@echo "$$REDIS_TEST_CONFIG" | $(REDIS_SERVER) -
|
||||
$(PRE) ./hiredis-test -h 127.0.0.1 -p $(REDIS_PORT) -s /tmp/hiredis-test-redis.sock || \
|
||||
( kill `cat /tmp/hiredis-test-redis.pid` && false )
|
||||
kill `cat /tmp/hiredis-test-redis.pid`
|
||||
|
||||
.c.o:
|
||||
$(CC) -std=c99 -pedantic -c $(REAL_CFLAGS) $<
|
||||
|
||||
clean:
|
||||
rm -rf $(DYLIBNAME) $(STLIBNAME) $(SSL_DYLIBNAME) $(SSL_STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
|
||||
rm -rf $(DYLIBNAME) $(STLIBNAME) $(TESTS) $(PKGCONFNAME) examples/hiredis-example* *.o *.gcda *.gcno *.gcov
|
||||
|
||||
dep:
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -MM *.c
|
||||
$(CC) -MM *.c
|
||||
|
||||
INSTALL?= cp -pPR
|
||||
|
||||
@@ -221,20 +175,6 @@ $(PKGCONFNAME): hiredis.h
|
||||
@echo Libs: -L\$${libdir} -lhiredis >> $@
|
||||
@echo Cflags: -I\$${includedir} -D_FILE_OFFSET_BITS=64 >> $@
|
||||
|
||||
$(SSL_PKGCONFNAME): hiredis.h
|
||||
@echo "Generating $@ for pkgconfig..."
|
||||
@echo prefix=$(PREFIX) > $@
|
||||
@echo exec_prefix=\$${prefix} >> $@
|
||||
@echo libdir=$(PREFIX)/$(LIBRARY_PATH) >> $@
|
||||
@echo includedir=$(PREFIX)/$(INCLUDE_PATH) >> $@
|
||||
@echo >> $@
|
||||
@echo Name: hiredis_ssl >> $@
|
||||
@echo Description: SSL Support for hiredis. >> $@
|
||||
@echo Version: $(HIREDIS_MAJOR).$(HIREDIS_MINOR).$(HIREDIS_PATCH) >> $@
|
||||
@echo Requires: hiredis >> $@
|
||||
@echo Libs: -L\$${libdir} -lhiredis_ssl >> $@
|
||||
@echo Libs.private: -lssl -lcrypto >> $@
|
||||
|
||||
install: $(DYLIBNAME) $(STLIBNAME) $(PKGCONFNAME)
|
||||
mkdir -p $(INSTALL_INCLUDE_PATH) $(INSTALL_INCLUDE_PATH)/adapters $(INSTALL_LIBRARY_PATH)
|
||||
$(INSTALL) hiredis.h async.h read.h sds.h $(INSTALL_INCLUDE_PATH)
|
||||
|
||||
Vendored
+1
-2
@@ -286,7 +286,6 @@ return `REDIS_ERR`. The function to set the disconnect callback has the followin
|
||||
```c
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
```
|
||||
`ac->data` may be used to pass user data to this callback, the same can be done for redisConnectCallback.
|
||||
### Sending commands and their callbacks
|
||||
|
||||
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
|
||||
@@ -407,6 +406,6 @@ as soon as possible in order to prevent allocation of useless memory.
|
||||
## AUTHORS
|
||||
|
||||
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
|
||||
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.
|
||||
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.
|
||||
Hiredis is currently maintained by Matt Stancliff (matt at genges dot com) and
|
||||
Jan-Erik Rediger (janerik at fnordig dot com)
|
||||
Vendored
+24
-88
@@ -34,113 +34,48 @@
|
||||
#include "../hiredis.h"
|
||||
#include "../async.h"
|
||||
|
||||
#define REDIS_LIBEVENT_DELETED 0x01
|
||||
#define REDIS_LIBEVENT_ENTERED 0x02
|
||||
|
||||
typedef struct redisLibeventEvents {
|
||||
redisAsyncContext *context;
|
||||
struct event *ev;
|
||||
struct event_base *base;
|
||||
struct timeval tv;
|
||||
short flags;
|
||||
short state;
|
||||
struct event *rev, *wev;
|
||||
} redisLibeventEvents;
|
||||
|
||||
static void redisLibeventDestroy(redisLibeventEvents *e) {
|
||||
free(e);
|
||||
}
|
||||
|
||||
static void redisLibeventHandler(int fd, short event, void *arg) {
|
||||
((void)fd);
|
||||
static void redisLibeventReadEvent(int fd, short event, void *arg) {
|
||||
((void)fd); ((void)event);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)arg;
|
||||
e->state |= REDIS_LIBEVENT_ENTERED;
|
||||
|
||||
#define CHECK_DELETED() if (e->state & REDIS_LIBEVENT_DELETED) {\
|
||||
redisLibeventDestroy(e);\
|
||||
return; \
|
||||
}
|
||||
|
||||
if ((event & EV_TIMEOUT) && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleTimeout(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
if ((event & EV_READ) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleRead(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
if ((event & EV_WRITE) && e->context && (e->state & REDIS_LIBEVENT_DELETED) == 0) {
|
||||
redisAsyncHandleWrite(e->context);
|
||||
CHECK_DELETED();
|
||||
}
|
||||
|
||||
e->state &= ~REDIS_LIBEVENT_ENTERED;
|
||||
#undef CHECK_DELETED
|
||||
redisAsyncHandleRead(e->context);
|
||||
}
|
||||
|
||||
static void redisLibeventUpdate(void *privdata, short flag, int isRemove) {
|
||||
redisLibeventEvents *e = (redisLibeventEvents *)privdata;
|
||||
const struct timeval *tv = e->tv.tv_sec || e->tv.tv_usec ? &e->tv : NULL;
|
||||
|
||||
if (isRemove) {
|
||||
if ((e->flags & flag) == 0) {
|
||||
return;
|
||||
} else {
|
||||
e->flags &= ~flag;
|
||||
}
|
||||
} else {
|
||||
if (e->flags & flag) {
|
||||
return;
|
||||
} else {
|
||||
e->flags |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
event_del(e->ev);
|
||||
event_assign(e->ev, e->base, e->context->c.fd, e->flags | EV_PERSIST,
|
||||
redisLibeventHandler, privdata);
|
||||
event_add(e->ev, tv);
|
||||
static void redisLibeventWriteEvent(int fd, short event, void *arg) {
|
||||
((void)fd); ((void)event);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)arg;
|
||||
redisAsyncHandleWrite(e->context);
|
||||
}
|
||||
|
||||
static void redisLibeventAddRead(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_READ, 0);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
||||
event_add(e->rev,NULL);
|
||||
}
|
||||
|
||||
static void redisLibeventDelRead(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_READ, 1);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
||||
event_del(e->rev);
|
||||
}
|
||||
|
||||
static void redisLibeventAddWrite(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_WRITE, 0);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
||||
event_add(e->wev,NULL);
|
||||
}
|
||||
|
||||
static void redisLibeventDelWrite(void *privdata) {
|
||||
redisLibeventUpdate(privdata, EV_WRITE, 1);
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
||||
event_del(e->wev);
|
||||
}
|
||||
|
||||
static void redisLibeventCleanup(void *privdata) {
|
||||
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
|
||||
if (!e) {
|
||||
return;
|
||||
}
|
||||
event_del(e->ev);
|
||||
event_free(e->ev);
|
||||
e->ev = NULL;
|
||||
|
||||
if (e->state & REDIS_LIBEVENT_ENTERED) {
|
||||
e->state |= REDIS_LIBEVENT_DELETED;
|
||||
} else {
|
||||
redisLibeventDestroy(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisLibeventSetTimeout(void *privdata, struct timeval tv) {
|
||||
redisLibeventEvents *e = (redisLibeventEvents *)privdata;
|
||||
short flags = e->flags;
|
||||
e->flags = 0;
|
||||
e->tv = tv;
|
||||
redisLibeventUpdate(e, flags, 0);
|
||||
event_free(e->rev);
|
||||
event_free(e->wev);
|
||||
free(e);
|
||||
}
|
||||
|
||||
static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
||||
@@ -152,7 +87,7 @@ static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
||||
return REDIS_ERR;
|
||||
|
||||
/* Create container for context and r/w events */
|
||||
e = (redisLibeventEvents*)calloc(1, sizeof(*e));
|
||||
e = (redisLibeventEvents*)malloc(sizeof(*e));
|
||||
e->context = ac;
|
||||
|
||||
/* Register functions to start/stop listening for events */
|
||||
@@ -161,12 +96,13 @@ static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
|
||||
ac->ev.addWrite = redisLibeventAddWrite;
|
||||
ac->ev.delWrite = redisLibeventDelWrite;
|
||||
ac->ev.cleanup = redisLibeventCleanup;
|
||||
ac->ev.scheduleTimer = redisLibeventSetTimeout;
|
||||
ac->ev.data = e;
|
||||
|
||||
/* Initialize and install read/write events */
|
||||
e->ev = event_new(base, c->fd, EV_READ | EV_WRITE, redisLibeventHandler, e);
|
||||
e->base = base;
|
||||
e->rev = event_new(base, c->fd, EV_READ, redisLibeventReadEvent, e);
|
||||
e->wev = event_new(base, c->fd, EV_WRITE, redisLibeventWriteEvent, e);
|
||||
event_add(e->rev, NULL);
|
||||
event_add(e->wev, NULL);
|
||||
return REDIS_OK;
|
||||
}
|
||||
#endif
|
||||
Vendored
+3
-4
@@ -5,9 +5,8 @@ environment:
|
||||
CC: gcc
|
||||
- CYG_BASH: C:\cygwin\bin\bash
|
||||
CC: gcc
|
||||
CFLAGS: -m32
|
||||
CXXFLAGS: -m32
|
||||
LDFLAGS: -m32
|
||||
TARGET: 32bit
|
||||
TARGET_VARS: 32bit-vars
|
||||
|
||||
clone_depth: 1
|
||||
|
||||
@@ -21,4 +20,4 @@ install:
|
||||
|
||||
build_script:
|
||||
- 'echo building...'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; mkdir build && cd build && cmake .. -G \"Unix Makefiles\" && make VERBOSE=1"'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; make LDFLAGS=$LDFLAGS CC=$CC $TARGET CFLAGS=$CFLAGS && make LDFLAGS=$LDFLAGS CC=$CC $TARGET_VARS hiredis-example"'
|
||||
Vendored
+63
-113
@@ -32,9 +32,7 @@
|
||||
#include "fmacros.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
@@ -42,9 +40,22 @@
|
||||
#include "net.h"
|
||||
#include "dict.c"
|
||||
#include "sds.h"
|
||||
#include "win32.h"
|
||||
|
||||
#include "async_private.h"
|
||||
#define _EL_ADD_READ(ctx) do { \
|
||||
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_DEL_READ(ctx) do { \
|
||||
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_ADD_WRITE(ctx) do { \
|
||||
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_DEL_WRITE(ctx) do { \
|
||||
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_CLEANUP(ctx) do { \
|
||||
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
|
||||
} while(0);
|
||||
|
||||
/* Forward declaration of function in hiredis.c */
|
||||
int __redisAppendCommand(redisContext *c, const char *cmd, size_t len);
|
||||
@@ -115,7 +126,6 @@ static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
|
||||
ac->ev.addWrite = NULL;
|
||||
ac->ev.delWrite = NULL;
|
||||
ac->ev.cleanup = NULL;
|
||||
ac->ev.scheduleTimer = NULL;
|
||||
|
||||
ac->onConnect = NULL;
|
||||
ac->onDisconnect = NULL;
|
||||
@@ -140,52 +150,56 @@ static void __redisAsyncCopyError(redisAsyncContext *ac) {
|
||||
ac->errstr = c->errstr;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options) {
|
||||
redisOptions myOptions = *options;
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
|
||||
redisContext *c;
|
||||
redisAsyncContext *ac;
|
||||
|
||||
myOptions.options |= REDIS_OPT_NONBLOCK;
|
||||
c = redisConnectWithOptions(&myOptions);
|
||||
if (c == NULL) {
|
||||
c = redisConnectNonBlock(ip,port);
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ac = redisAsyncInitialize(c);
|
||||
if (ac == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.endpoint.tcp.source_addr = source_addr;
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
redisContext *c = redisConnectBindNonBlock(ip,port,source_addr);
|
||||
redisAsyncContext *ac = redisAsyncInitialize(c);
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.options |= REDIS_OPT_REUSEADDR;
|
||||
options.endpoint.tcp.source_addr = source_addr;
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
redisContext *c = redisConnectBindNonBlockWithReuse(ip,port,source_addr);
|
||||
redisAsyncContext *ac = redisAsyncInitialize(c);
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
||||
return redisAsyncConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
redisAsyncContext *ac;
|
||||
|
||||
c = redisConnectUnixNonBlock(path);
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
ac = redisAsyncInitialize(c);
|
||||
if (ac == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__redisAsyncCopyError(ac);
|
||||
return ac;
|
||||
}
|
||||
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
|
||||
@@ -314,7 +328,7 @@ void redisAsyncFree(redisAsyncContext *ac) {
|
||||
}
|
||||
|
||||
/* Helper function to make the disconnect happen and clean up. */
|
||||
void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
static void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
/* Make sure error is accessible if there is any */
|
||||
@@ -330,15 +344,9 @@ void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
}
|
||||
|
||||
/* cleanup event library on disconnect.
|
||||
* this is safe to call multiple times */
|
||||
_EL_CLEANUP(ac);
|
||||
|
||||
/* For non-clean disconnects, __redisAsyncFree() will execute pending
|
||||
* callbacks with a NULL-reply. */
|
||||
if (!(c->flags & REDIS_NO_AUTO_FREE)) {
|
||||
__redisAsyncFree(ac);
|
||||
}
|
||||
__redisAsyncFree(ac);
|
||||
}
|
||||
|
||||
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
|
||||
@@ -350,9 +358,6 @@ void __redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
c->flags |= REDIS_DISCONNECTING;
|
||||
|
||||
/** unset the auto-free flag here, because disconnect undoes this */
|
||||
c->flags &= ~REDIS_NO_AUTO_FREE;
|
||||
if (!(c->flags & REDIS_IN_CALLBACK) && ac->replies.head == NULL)
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
@@ -403,7 +408,7 @@ static int __redisGetSubscribeCallback(redisAsyncContext *ac, redisReply *reply,
|
||||
assert(reply->element[2]->type == REDIS_REPLY_INTEGER);
|
||||
|
||||
/* Unset subscribed flag only when no pipelined pending subscribe. */
|
||||
if (reply->element[2]->integer == 0
|
||||
if (reply->element[2]->integer == 0
|
||||
&& dictSize(ac->sub.channels) == 0
|
||||
&& dictSize(ac->sub.patterns) == 0)
|
||||
c->flags &= ~REDIS_SUBSCRIBED;
|
||||
@@ -519,18 +524,6 @@ static int __redisAsyncHandleConnect(redisAsyncContext *ac) {
|
||||
}
|
||||
}
|
||||
|
||||
void redisAsyncRead(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (redisBufferRead(c) == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
/* Always re-schedule reads */
|
||||
_EL_ADD_READ(ac);
|
||||
redisProcessCallbacks(ac);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function should be called when the socket is readable.
|
||||
* It processes all replies that can be read and executes their callbacks.
|
||||
*/
|
||||
@@ -546,13 +539,28 @@ void redisAsyncHandleRead(redisAsyncContext *ac) {
|
||||
return;
|
||||
}
|
||||
|
||||
c->funcs->async_read(ac);
|
||||
if (redisBufferRead(c) == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
/* Always re-schedule reads */
|
||||
_EL_ADD_READ(ac);
|
||||
redisProcessCallbacks(ac);
|
||||
}
|
||||
}
|
||||
|
||||
void redisAsyncWrite(redisAsyncContext *ac) {
|
||||
void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
int done = 0;
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
/* Abort connect was not successful. */
|
||||
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
|
||||
return;
|
||||
/* Try again later when the context is still not connected. */
|
||||
if (!(c->flags & REDIS_CONNECTED))
|
||||
return;
|
||||
}
|
||||
|
||||
if (redisBufferWrite(c,&done) == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
@@ -567,51 +575,6 @@ void redisAsyncWrite(redisAsyncContext *ac) {
|
||||
}
|
||||
}
|
||||
|
||||
void redisAsyncHandleWrite(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED)) {
|
||||
/* Abort connect was not successful. */
|
||||
if (__redisAsyncHandleConnect(ac) != REDIS_OK)
|
||||
return;
|
||||
/* Try again later when the context is still not connected. */
|
||||
if (!(c->flags & REDIS_CONNECTED))
|
||||
return;
|
||||
}
|
||||
|
||||
c->funcs->async_write(ac);
|
||||
}
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
void redisAsyncHandleTimeout(redisAsyncContext *ac) {
|
||||
redisContext *c = &(ac->c);
|
||||
redisCallback cb;
|
||||
|
||||
if ((c->flags & REDIS_CONNECTED) && ac->replies.head == NULL) {
|
||||
/* Nothing to do - just an idle timeout */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!c->err) {
|
||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "Timeout");
|
||||
}
|
||||
|
||||
if (!(c->flags & REDIS_CONNECTED) && ac->onConnect) {
|
||||
ac->onConnect(ac, REDIS_ERR);
|
||||
}
|
||||
|
||||
while (__redisShiftCallback(&ac->replies, &cb) == REDIS_OK) {
|
||||
__redisRunCallback(ac, &cb, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: Don't automatically sever the connection,
|
||||
* rather, allow to ignore <x> responses before the queue is clear
|
||||
*/
|
||||
__redisAsyncDisconnect(ac);
|
||||
}
|
||||
|
||||
/* Sets a pointer to the first argument and its length starting at p. Returns
|
||||
* the number of bytes to skip to get to the following argument. */
|
||||
static const char *nextArgument(const char *start, const char **str, size_t *len) {
|
||||
@@ -751,16 +714,3 @@ int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void
|
||||
int status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
|
||||
return status;
|
||||
}
|
||||
|
||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv) {
|
||||
if (!ac->c.timeout) {
|
||||
ac->c.timeout = calloc(1, sizeof(tv));
|
||||
}
|
||||
|
||||
if (tv.tv_sec == ac->c.timeout->tv_sec &&
|
||||
tv.tv_usec == ac->c.timeout->tv_usec) {
|
||||
return;
|
||||
}
|
||||
|
||||
*ac->c.timeout = tv;
|
||||
}
|
||||
Vendored
-8
@@ -57,7 +57,6 @@ typedef struct redisCallbackList {
|
||||
/* Connection callback prototypes */
|
||||
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
|
||||
typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
|
||||
typedef void(redisTimerCallback)(void *timer, void *privdata);
|
||||
|
||||
/* Context for an async connection to Redis */
|
||||
typedef struct redisAsyncContext {
|
||||
@@ -82,7 +81,6 @@ typedef struct redisAsyncContext {
|
||||
void (*addWrite)(void *privdata);
|
||||
void (*delWrite)(void *privdata);
|
||||
void (*cleanup)(void *privdata);
|
||||
void (*scheduleTimer)(void *privdata, struct timeval tv);
|
||||
} ev;
|
||||
|
||||
/* Called when either the connection is terminated due to an error or per
|
||||
@@ -108,7 +106,6 @@ typedef struct redisAsyncContext {
|
||||
} redisAsyncContext;
|
||||
|
||||
/* Functions that proxy to hiredis */
|
||||
redisAsyncContext *redisAsyncConnectWithOptions(const redisOptions *options);
|
||||
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
|
||||
redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
|
||||
redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
@@ -116,17 +113,12 @@ redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
|
||||
redisAsyncContext *redisAsyncConnectUnix(const char *path);
|
||||
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
|
||||
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
|
||||
|
||||
void redisAsyncSetTimeout(redisAsyncContext *ac, struct timeval tv);
|
||||
void redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisAsyncFree(redisAsyncContext *ac);
|
||||
|
||||
/* Handle read/write events */
|
||||
void redisAsyncHandleRead(redisAsyncContext *ac);
|
||||
void redisAsyncHandleWrite(redisAsyncContext *ac);
|
||||
void redisAsyncHandleTimeout(redisAsyncContext *ac);
|
||||
void redisAsyncRead(redisAsyncContext *ac);
|
||||
void redisAsyncWrite(redisAsyncContext *ac);
|
||||
|
||||
/* Command functions for an async context. Write the command to the
|
||||
* output buffer and register the provided callback. */
|
||||
|
||||
Vendored
-72
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_ASYNC_PRIVATE_H
|
||||
#define __HIREDIS_ASYNC_PRIVATE_H
|
||||
|
||||
#define _EL_ADD_READ(ctx) \
|
||||
do { \
|
||||
refreshTimeout(ctx); \
|
||||
if ((ctx)->ev.addRead) (ctx)->ev.addRead((ctx)->ev.data); \
|
||||
} while (0)
|
||||
#define _EL_DEL_READ(ctx) do { \
|
||||
if ((ctx)->ev.delRead) (ctx)->ev.delRead((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_ADD_WRITE(ctx) \
|
||||
do { \
|
||||
refreshTimeout(ctx); \
|
||||
if ((ctx)->ev.addWrite) (ctx)->ev.addWrite((ctx)->ev.data); \
|
||||
} while (0)
|
||||
#define _EL_DEL_WRITE(ctx) do { \
|
||||
if ((ctx)->ev.delWrite) (ctx)->ev.delWrite((ctx)->ev.data); \
|
||||
} while(0)
|
||||
#define _EL_CLEANUP(ctx) do { \
|
||||
if ((ctx)->ev.cleanup) (ctx)->ev.cleanup((ctx)->ev.data); \
|
||||
ctx->ev.cleanup = NULL; \
|
||||
} while(0);
|
||||
|
||||
static inline void refreshTimeout(redisAsyncContext *ctx) {
|
||||
if (ctx->c.timeout && ctx->ev.scheduleTimer &&
|
||||
(ctx->c.timeout->tv_sec || ctx->c.timeout->tv_usec)) {
|
||||
ctx->ev.scheduleTimer(ctx->ev.data, *ctx->c.timeout);
|
||||
// } else {
|
||||
// printf("Not scheduling timer.. (tmo=%p)\n", ctx->c.timeout);
|
||||
// if (ctx->c.timeout){
|
||||
// printf("tv_sec: %u. tv_usec: %u\n", ctx->c.timeout->tv_sec,
|
||||
// ctx->c.timeout->tv_usec);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void __redisAsyncDisconnect(redisAsyncContext *ac);
|
||||
void redisProcessCallbacks(redisAsyncContext *ac);
|
||||
|
||||
#endif /* __HIREDIS_ASYNC_PRIVATE_H */
|
||||
Vendored
-46
@@ -1,46 +0,0 @@
|
||||
INCLUDE(FindPkgConfig)
|
||||
# Check for GLib
|
||||
|
||||
PKG_CHECK_MODULES(GLIB2 glib-2.0)
|
||||
if (GLIB2_FOUND)
|
||||
INCLUDE_DIRECTORIES(${GLIB2_INCLUDE_DIRS})
|
||||
LINK_DIRECTORIES(${GLIB2_LIBRARY_DIRS})
|
||||
ADD_EXECUTABLE(example-glib example-glib.c)
|
||||
TARGET_LINK_LIBRARIES(example-glib hiredis ${GLIB2_LIBRARIES})
|
||||
ENDIF(GLIB2_FOUND)
|
||||
|
||||
FIND_PATH(LIBEV ev.h
|
||||
HINTS /usr/local /usr/opt/local
|
||||
ENV LIBEV_INCLUDE_DIR)
|
||||
|
||||
if (LIBEV)
|
||||
# Just compile and link with libev
|
||||
ADD_EXECUTABLE(example-libev example-libev.c)
|
||||
TARGET_LINK_LIBRARIES(example-libev hiredis ev)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBEVENT event.h)
|
||||
if (LIBEVENT)
|
||||
ADD_EXECUTABLE(example-libevent example-libevent)
|
||||
TARGET_LINK_LIBRARIES(example-libevent hiredis event)
|
||||
ENDIF()
|
||||
|
||||
FIND_PATH(LIBUV uv.h)
|
||||
IF (LIBUV)
|
||||
ADD_EXECUTABLE(example-libuv example-libuv.c)
|
||||
TARGET_LINK_LIBRARIES(example-libuv hiredis uv)
|
||||
ENDIF()
|
||||
|
||||
IF (APPLE)
|
||||
FIND_LIBRARY(CF CoreFoundation)
|
||||
ADD_EXECUTABLE(example-macosx example-macosx.c)
|
||||
TARGET_LINK_LIBRARIES(example-macosx hiredis ${CF})
|
||||
ENDIF()
|
||||
|
||||
IF (ENABLE_SSL)
|
||||
ADD_EXECUTABLE(example-ssl example-ssl.c)
|
||||
TARGET_LINK_LIBRARIES(example-ssl hiredis hiredis_ssl)
|
||||
ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(example example.c)
|
||||
TARGET_LINK_LIBRARIES(example hiredis)
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <hiredis_ssl.h>
|
||||
#include <async.h>
|
||||
#include <adapters/libevent.h>
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
redisAsyncDisconnect(c);
|
||||
}
|
||||
|
||||
void connectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Connected...\n");
|
||||
}
|
||||
|
||||
void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
if (status != REDIS_OK) {
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return;
|
||||
}
|
||||
printf("Disconnected...\n");
|
||||
}
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
struct event_base *base = event_base_new();
|
||||
if (argc < 5) {
|
||||
fprintf(stderr,
|
||||
"Usage: %s <key> <host> <port> <cert> <certKey> [ca]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char *value = argv[1];
|
||||
size_t nvalue = strlen(value);
|
||||
|
||||
const char *hostname = argv[2];
|
||||
int port = atoi(argv[3]);
|
||||
|
||||
const char *cert = argv[4];
|
||||
const char *certKey = argv[5];
|
||||
const char *caCert = argc > 5 ? argv[6] : NULL;
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnect(hostname, port);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
return 1;
|
||||
}
|
||||
if (redisSecureConnection(&c->c, caCert, cert, certKey, "sni") != REDIS_OK) {
|
||||
printf("SSL Error!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
redisLibeventAttach(c,base);
|
||||
redisAsyncSetConnectCallback(c,connectCallback);
|
||||
redisAsyncSetDisconnectCallback(c,disconnectCallback);
|
||||
redisAsyncCommand(c, NULL, NULL, "SET key %b", value, nvalue);
|
||||
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
|
||||
event_base_dispatch(base);
|
||||
return 0;
|
||||
}
|
||||
+2
-13
@@ -9,12 +9,7 @@
|
||||
|
||||
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
|
||||
redisReply *reply = r;
|
||||
if (reply == NULL) {
|
||||
if (c->errstr) {
|
||||
printf("errstr: %s\n", c->errstr);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (reply == NULL) return;
|
||||
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
|
||||
|
||||
/* Disconnect after receiving the reply to GET */
|
||||
@@ -40,14 +35,8 @@ void disconnectCallback(const redisAsyncContext *c, int status) {
|
||||
int main (int argc, char **argv) {
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
struct event_base *base = event_base_new();
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, "127.0.0.1", 6379);
|
||||
struct timeval tv = {0};
|
||||
tv.tv_sec = 1;
|
||||
options.timeout = &tv;
|
||||
|
||||
|
||||
redisAsyncContext *c = redisAsyncConnectWithOptions(&options);
|
||||
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
|
||||
if (c->err) {
|
||||
/* Let *c leak for now... */
|
||||
printf("Error: %s\n", c->errstr);
|
||||
|
||||
Vendored
-97
@@ -1,97 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <hiredis.h>
|
||||
#include <hiredis_ssl.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
if (argc < 4) {
|
||||
printf("Usage: %s <host> <port> <cert> <key> [ca]\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
int port = atoi(argv[2]);
|
||||
const char *cert = argv[3];
|
||||
const char *key = argv[4];
|
||||
const char *ca = argc > 4 ? argv[5] : NULL;
|
||||
|
||||
struct timeval tv = { 1, 500000 }; // 1.5 seconds
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, hostname, port);
|
||||
options.timeout = &tv;
|
||||
c = redisConnectWithOptions(&options);
|
||||
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
} else {
|
||||
printf("Connection error: can't allocate redis context\n");
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (redisSecureConnection(c, ca, cert, key, "sni") != REDIS_OK) {
|
||||
printf("Couldn't initialize SSL!\n");
|
||||
printf("Error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* PING server */
|
||||
reply = redisCommand(c,"PING");
|
||||
printf("PING: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Set a key */
|
||||
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
|
||||
printf("SET: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Set a key using binary safe API */
|
||||
reply = redisCommand(c,"SET %b %b", "bar", (size_t) 3, "hello", (size_t) 5);
|
||||
printf("SET (binary API): %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Try a GET and two INCR */
|
||||
reply = redisCommand(c,"GET foo");
|
||||
printf("GET foo: %s\n", reply->str);
|
||||
freeReplyObject(reply);
|
||||
|
||||
reply = redisCommand(c,"INCR counter");
|
||||
printf("INCR counter: %lld\n", reply->integer);
|
||||
freeReplyObject(reply);
|
||||
/* again ... */
|
||||
reply = redisCommand(c,"INCR counter");
|
||||
printf("INCR counter: %lld\n", reply->integer);
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Create a list of numbers, from 0 to 9 */
|
||||
reply = redisCommand(c,"DEL mylist");
|
||||
freeReplyObject(reply);
|
||||
for (j = 0; j < 10; j++) {
|
||||
char buf[64];
|
||||
|
||||
snprintf(buf,64,"%u",j);
|
||||
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
|
||||
freeReplyObject(reply);
|
||||
}
|
||||
|
||||
/* Let's check what we have inside the list */
|
||||
reply = redisCommand(c,"LRANGE mylist 0 -1");
|
||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||
for (j = 0; j < reply->elements; j++) {
|
||||
printf("%u) %s\n", j, reply->element[j]->str);
|
||||
}
|
||||
}
|
||||
freeReplyObject(reply);
|
||||
|
||||
/* Disconnects and frees the context */
|
||||
redisFree(c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Vendored
+2
-15
@@ -5,27 +5,14 @@
|
||||
#include <hiredis.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned int j, isunix = 0;
|
||||
unsigned int j;
|
||||
redisContext *c;
|
||||
redisReply *reply;
|
||||
const char *hostname = (argc > 1) ? argv[1] : "127.0.0.1";
|
||||
|
||||
if (argc > 2) {
|
||||
if (*argv[2] == 'u' || *argv[2] == 'U') {
|
||||
isunix = 1;
|
||||
/* in this case, host is the path to the unix socket */
|
||||
printf("Will connect to unix socket @%s\n", hostname);
|
||||
}
|
||||
}
|
||||
|
||||
int port = (argc > 2) ? atoi(argv[2]) : 6379;
|
||||
|
||||
struct timeval timeout = { 1, 500000 }; // 1.5 seconds
|
||||
if (isunix) {
|
||||
c = redisConnectUnixWithTimeout(hostname, timeout);
|
||||
} else {
|
||||
c = redisConnectWithTimeout(hostname, port, timeout);
|
||||
}
|
||||
c = redisConnectWithTimeout(hostname, port, timeout);
|
||||
if (c == NULL || c->err) {
|
||||
if (c) {
|
||||
printf("Connection error: %s\n", c->errstr);
|
||||
|
||||
Vendored
+122
-134
@@ -34,6 +34,7 @@
|
||||
#include "fmacros.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
@@ -41,20 +42,10 @@
|
||||
#include "hiredis.h"
|
||||
#include "net.h"
|
||||
#include "sds.h"
|
||||
#include "async.h"
|
||||
#include "win32.h"
|
||||
|
||||
static redisContextFuncs redisContextDefaultFuncs = {
|
||||
.free_privdata = NULL,
|
||||
.async_read = redisAsyncRead,
|
||||
.async_write = redisAsyncWrite,
|
||||
.read = redisNetRead,
|
||||
.write = redisNetWrite
|
||||
};
|
||||
|
||||
static redisReply *createReplyObject(int type);
|
||||
static void *createStringObject(const redisReadTask *task, char *str, size_t len);
|
||||
static void *createArrayObject(const redisReadTask *task, size_t elements);
|
||||
static void *createArrayObject(const redisReadTask *task, int elements);
|
||||
static void *createIntegerObject(const redisReadTask *task, long long value);
|
||||
static void *createDoubleObject(const redisReadTask *task, double value, char *str, size_t len);
|
||||
static void *createNilObject(const redisReadTask *task);
|
||||
@@ -121,34 +112,21 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||
if (r == NULL)
|
||||
return NULL;
|
||||
|
||||
buf = malloc(len+1);
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(task->type == REDIS_REPLY_ERROR ||
|
||||
task->type == REDIS_REPLY_STATUS ||
|
||||
task->type == REDIS_REPLY_STRING ||
|
||||
task->type == REDIS_REPLY_VERB);
|
||||
task->type == REDIS_REPLY_STRING);
|
||||
|
||||
/* Copy string value */
|
||||
if (task->type == REDIS_REPLY_VERB) {
|
||||
buf = malloc(len-4+1); /* Skip 4 bytes of verbatim type header. */
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(r->vtype,str,3);
|
||||
r->vtype[3] = '\0';
|
||||
memcpy(buf,str+4,len-4);
|
||||
buf[len-4] = '\0';
|
||||
r->len = len-4;
|
||||
} else {
|
||||
buf = malloc(len+1);
|
||||
if (buf == NULL) {
|
||||
freeReplyObject(r);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(buf,str,len);
|
||||
buf[len] = '\0';
|
||||
r->len = len;
|
||||
}
|
||||
memcpy(buf,str,len);
|
||||
buf[len] = '\0';
|
||||
r->str = buf;
|
||||
r->len = len;
|
||||
|
||||
if (task->parent) {
|
||||
parent = task->parent->obj;
|
||||
@@ -160,7 +138,7 @@ static void *createStringObject(const redisReadTask *task, char *str, size_t len
|
||||
return r;
|
||||
}
|
||||
|
||||
static void *createArrayObject(const redisReadTask *task, size_t elements) {
|
||||
static void *createArrayObject(const redisReadTask *task, int elements) {
|
||||
redisReply *r, *parent;
|
||||
|
||||
r = createReplyObject(task->type);
|
||||
@@ -671,30 +649,29 @@ redisReader *redisReaderCreate(void) {
|
||||
return redisReaderCreateWithFunctions(&defaultFunctions);
|
||||
}
|
||||
|
||||
static redisContext *redisContextInit(const redisOptions *options) {
|
||||
static redisContext *redisContextInit(void) {
|
||||
redisContext *c;
|
||||
|
||||
c = calloc(1, sizeof(*c));
|
||||
c = calloc(1,sizeof(redisContext));
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->funcs = &redisContextDefaultFuncs;
|
||||
c->obuf = sdsempty();
|
||||
c->reader = redisReaderCreate();
|
||||
c->fd = REDIS_INVALID_FD;
|
||||
|
||||
if (c->obuf == NULL || c->reader == NULL) {
|
||||
redisFree(c);
|
||||
return NULL;
|
||||
}
|
||||
(void)options; /* options are used in other functions */
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void redisFree(redisContext *c) {
|
||||
if (c == NULL)
|
||||
return;
|
||||
redisNetClose(c);
|
||||
if (c->fd > 0)
|
||||
close(c->fd);
|
||||
|
||||
sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
@@ -703,16 +680,12 @@ void redisFree(redisContext *c) {
|
||||
free(c->unix_sock.path);
|
||||
free(c->timeout);
|
||||
free(c->saddr);
|
||||
if (c->funcs->free_privdata) {
|
||||
c->funcs->free_privdata(c->privdata);
|
||||
}
|
||||
memset(c, 0xff, sizeof(*c));
|
||||
free(c);
|
||||
}
|
||||
|
||||
redisFD redisFreeKeepFd(redisContext *c) {
|
||||
redisFD fd = c->fd;
|
||||
c->fd = REDIS_INVALID_FD;
|
||||
int redisFreeKeepFd(redisContext *c) {
|
||||
int fd = c->fd;
|
||||
c->fd = -1;
|
||||
redisFree(c);
|
||||
return fd;
|
||||
}
|
||||
@@ -721,13 +694,10 @@ int redisReconnect(redisContext *c) {
|
||||
c->err = 0;
|
||||
memset(c->errstr, '\0', strlen(c->errstr));
|
||||
|
||||
if (c->privdata && c->funcs->free_privdata) {
|
||||
c->funcs->free_privdata(c->privdata);
|
||||
c->privdata = NULL;
|
||||
if (c->fd > 0) {
|
||||
close(c->fd);
|
||||
}
|
||||
|
||||
redisNetClose(c);
|
||||
|
||||
sdsfree(c->obuf);
|
||||
redisReaderFree(c->reader);
|
||||
|
||||
@@ -748,107 +718,112 @@ int redisReconnect(redisContext *c) {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
redisContext *redisConnectWithOptions(const redisOptions *options) {
|
||||
redisContext *c = redisContextInit(options);
|
||||
if (c == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (!(options->options & REDIS_OPT_NONBLOCK)) {
|
||||
c->flags |= REDIS_BLOCK;
|
||||
}
|
||||
if (options->options & REDIS_OPT_REUSEADDR) {
|
||||
c->flags |= REDIS_REUSEADDR;
|
||||
}
|
||||
if (options->options & REDIS_OPT_NOAUTOFREE) {
|
||||
c->flags |= REDIS_NO_AUTO_FREE;
|
||||
}
|
||||
|
||||
if (options->type == REDIS_CONN_TCP) {
|
||||
redisContextConnectBindTcp(c, options->endpoint.tcp.ip,
|
||||
options->endpoint.tcp.port, options->timeout,
|
||||
options->endpoint.tcp.source_addr);
|
||||
} else if (options->type == REDIS_CONN_UNIX) {
|
||||
redisContextConnectUnix(c, options->endpoint.unix_socket,
|
||||
options->timeout);
|
||||
} else if (options->type == REDIS_CONN_USERFD) {
|
||||
c->fd = options->endpoint.fd;
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
} else {
|
||||
// Unknown type - FIXME - FREE
|
||||
return NULL;
|
||||
}
|
||||
if (options->timeout != NULL && (c->flags & REDIS_BLOCK) && c->fd != REDIS_INVALID_FD) {
|
||||
redisContextSetTimeout(c, *options->timeout);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Connect to a Redis instance. On error the field error in the returned
|
||||
* context will be set to the return value of the error function.
|
||||
* When no set of reply functions is given, the default set will be used. */
|
||||
redisContext *redisConnect(const char *ip, int port) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectTcp(c,ip,port,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.timeout = &tv;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectTcp(c,ip,port,&tv);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.options |= REDIS_OPT_NONBLOCK;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
redisContextConnectTcp(c,ip,port,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectBindNonBlock(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.endpoint.tcp.source_addr = source_addr;
|
||||
options.options |= REDIS_OPT_NONBLOCK;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
||||
const char *source_addr) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_TCP(&options, ip, port);
|
||||
options.endpoint.tcp.source_addr = source_addr;
|
||||
options.options |= REDIS_OPT_NONBLOCK|REDIS_OPT_REUSEADDR;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
c->flags |= REDIS_REUSEADDR;
|
||||
redisContextConnectBindTcp(c,ip,port,NULL,source_addr);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectUnix(const char *path) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectUnix(c,path,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
||||
options.timeout = &tv;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->flags |= REDIS_BLOCK;
|
||||
redisContextConnectUnix(c,path,&tv);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectUnixNonBlock(const char *path) {
|
||||
redisOptions options = {0};
|
||||
REDIS_OPTIONS_SET_UNIX(&options, path);
|
||||
options.options |= REDIS_OPT_NONBLOCK;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->flags &= ~REDIS_BLOCK;
|
||||
redisContextConnectUnix(c,path,NULL);
|
||||
return c;
|
||||
}
|
||||
|
||||
redisContext *redisConnectFd(redisFD fd) {
|
||||
redisOptions options = {0};
|
||||
options.type = REDIS_CONN_USERFD;
|
||||
options.endpoint.fd = fd;
|
||||
return redisConnectWithOptions(&options);
|
||||
redisContext *redisConnectFd(int fd) {
|
||||
redisContext *c;
|
||||
|
||||
c = redisContextInit();
|
||||
if (c == NULL)
|
||||
return NULL;
|
||||
|
||||
c->fd = fd;
|
||||
c->flags |= REDIS_BLOCK | REDIS_CONNECTED;
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Set read/write timeout on a blocking socket. */
|
||||
@@ -878,15 +853,22 @@ int redisBufferRead(redisContext *c) {
|
||||
if (c->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
nread = c->funcs->read(c, buf, sizeof(buf));
|
||||
if (nread > 0) {
|
||||
if (redisReaderFeed(c->reader, buf, nread) != REDIS_OK) {
|
||||
__redisSetError(c, c->reader->err, c->reader->errstr);
|
||||
return REDIS_ERR;
|
||||
nread = read(c->fd,buf,sizeof(buf));
|
||||
if (nread == -1) {
|
||||
if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
} else {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
} else if (nread < 0) {
|
||||
} else if (nread == 0) {
|
||||
__redisSetError(c,REDIS_ERR_EOF,"Server closed the connection");
|
||||
return REDIS_ERR;
|
||||
} else {
|
||||
if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) {
|
||||
__redisSetError(c,c->reader->err,c->reader->errstr);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
@@ -901,15 +883,21 @@ int redisBufferRead(redisContext *c) {
|
||||
* c->errstr to hold the appropriate error string.
|
||||
*/
|
||||
int redisBufferWrite(redisContext *c, int *done) {
|
||||
int nwritten;
|
||||
|
||||
/* Return early when the context has seen an error. */
|
||||
if (c->err)
|
||||
return REDIS_ERR;
|
||||
|
||||
if (sdslen(c->obuf) > 0) {
|
||||
int nwritten = c->funcs->write(c);
|
||||
if (nwritten < 0) {
|
||||
return REDIS_ERR;
|
||||
nwritten = write(c->fd,c->obuf,sdslen(c->obuf));
|
||||
if (nwritten == -1) {
|
||||
if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
} else {
|
||||
__redisSetError(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
} else if (nwritten > 0) {
|
||||
if (nwritten == (signed)sdslen(c->obuf)) {
|
||||
sdsfree(c->obuf);
|
||||
|
||||
Vendored
+4
-99
@@ -35,11 +35,7 @@
|
||||
#define __HIREDIS_H
|
||||
#include "read.h"
|
||||
#include <stdarg.h> /* for va_list */
|
||||
#ifndef _MSC_VER
|
||||
#include <sys/time.h> /* for struct timeval */
|
||||
#else
|
||||
struct timeval; /* forward declaration */
|
||||
#endif
|
||||
#include <stdint.h> /* uintXX_t, etc */
|
||||
#include "sds.h" /* for sds */
|
||||
|
||||
@@ -78,12 +74,6 @@ struct timeval; /* forward declaration */
|
||||
/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
|
||||
#define REDIS_REUSEADDR 0x80
|
||||
|
||||
/**
|
||||
* Flag that indicates the user does not want the context to
|
||||
* be automatically freed upon error
|
||||
*/
|
||||
#define REDIS_NO_AUTO_FREE 0x200
|
||||
|
||||
#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
|
||||
|
||||
/* number of times we retry to connect in the case of EADDRNOTAVAIL and
|
||||
@@ -102,8 +92,6 @@ typedef struct redisReply {
|
||||
size_t len; /* Length of string */
|
||||
char *str; /* Used for REDIS_REPLY_ERROR, REDIS_REPLY_STRING
|
||||
and REDIS_REPLY_DOUBLE (in additionl to dval). */
|
||||
char vtype[4]; /* Used for REDIS_REPLY_VERB, contains the null
|
||||
terminated 3 character content type, such as "txt". */
|
||||
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
|
||||
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
|
||||
} redisReply;
|
||||
@@ -123,93 +111,14 @@ void redisFreeSdsCommand(sds cmd);
|
||||
|
||||
enum redisConnectionType {
|
||||
REDIS_CONN_TCP,
|
||||
REDIS_CONN_UNIX,
|
||||
REDIS_CONN_USERFD
|
||||
REDIS_CONN_UNIX
|
||||
};
|
||||
|
||||
struct redisSsl;
|
||||
|
||||
#define REDIS_OPT_NONBLOCK 0x01
|
||||
#define REDIS_OPT_REUSEADDR 0x02
|
||||
|
||||
/**
|
||||
* Don't automatically free the async object on a connection failure,
|
||||
* or other implicit conditions. Only free on an explicit call to disconnect() or free()
|
||||
*/
|
||||
#define REDIS_OPT_NOAUTOFREE 0x04
|
||||
|
||||
/* In Unix systems a file descriptor is a regular signed int, with -1
|
||||
* representing an invalid descriptor. In Windows it is a SOCKET
|
||||
* (32- or 64-bit unsigned integer depending on the architecture), where
|
||||
* all bits set (~0) is INVALID_SOCKET. */
|
||||
#ifndef _WIN32
|
||||
typedef int redisFD;
|
||||
#define REDIS_INVALID_FD -1
|
||||
#else
|
||||
#ifdef _WIN64
|
||||
typedef unsigned long long redisFD; /* SOCKET = 64-bit UINT_PTR */
|
||||
#else
|
||||
typedef unsigned long redisFD; /* SOCKET = 32-bit UINT_PTR */
|
||||
#endif
|
||||
#define REDIS_INVALID_FD ((redisFD)(~0)) /* INVALID_SOCKET */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
/*
|
||||
* the type of connection to use. This also indicates which
|
||||
* `endpoint` member field to use
|
||||
*/
|
||||
int type;
|
||||
/* bit field of REDIS_OPT_xxx */
|
||||
int options;
|
||||
/* timeout value. if NULL, no timeout is used */
|
||||
const struct timeval *timeout;
|
||||
union {
|
||||
/** use this field for tcp/ip connections */
|
||||
struct {
|
||||
const char *source_addr;
|
||||
const char *ip;
|
||||
int port;
|
||||
} tcp;
|
||||
/** use this field for unix domain sockets */
|
||||
const char *unix_socket;
|
||||
/**
|
||||
* use this field to have hiredis operate an already-open
|
||||
* file descriptor */
|
||||
redisFD fd;
|
||||
} endpoint;
|
||||
} redisOptions;
|
||||
|
||||
/**
|
||||
* Helper macros to initialize options to their specified fields.
|
||||
*/
|
||||
#define REDIS_OPTIONS_SET_TCP(opts, ip_, port_) \
|
||||
(opts)->type = REDIS_CONN_TCP; \
|
||||
(opts)->endpoint.tcp.ip = ip_; \
|
||||
(opts)->endpoint.tcp.port = port_;
|
||||
|
||||
#define REDIS_OPTIONS_SET_UNIX(opts, path) \
|
||||
(opts)->type = REDIS_CONN_UNIX; \
|
||||
(opts)->endpoint.unix_socket = path;
|
||||
|
||||
struct redisAsyncContext;
|
||||
struct redisContext;
|
||||
|
||||
typedef struct redisContextFuncs {
|
||||
void (*free_privdata)(void *);
|
||||
void (*async_read)(struct redisAsyncContext *);
|
||||
void (*async_write)(struct redisAsyncContext *);
|
||||
int (*read)(struct redisContext *, char *, size_t);
|
||||
int (*write)(struct redisContext *);
|
||||
} redisContextFuncs;
|
||||
|
||||
/* Context for a connection to Redis */
|
||||
typedef struct redisContext {
|
||||
const redisContextFuncs *funcs; /* Function table */
|
||||
|
||||
int err; /* Error flags, 0 when there is no error */
|
||||
char errstr[128]; /* String representation of error when applicable */
|
||||
redisFD fd;
|
||||
int fd;
|
||||
int flags;
|
||||
char *obuf; /* Write buffer */
|
||||
redisReader *reader; /* Protocol reader */
|
||||
@@ -230,12 +139,8 @@ typedef struct redisContext {
|
||||
/* For non-blocking connect */
|
||||
struct sockadr *saddr;
|
||||
size_t addrlen;
|
||||
|
||||
/* Additional private data for hiredis addons such as SSL */
|
||||
void *privdata;
|
||||
} redisContext;
|
||||
|
||||
redisContext *redisConnectWithOptions(const redisOptions *options);
|
||||
redisContext *redisConnect(const char *ip, int port);
|
||||
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
|
||||
redisContext *redisConnectNonBlock(const char *ip, int port);
|
||||
@@ -246,7 +151,7 @@ redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
|
||||
redisContext *redisConnectUnix(const char *path);
|
||||
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
|
||||
redisContext *redisConnectUnixNonBlock(const char *path);
|
||||
redisContext *redisConnectFd(redisFD fd);
|
||||
redisContext *redisConnectFd(int fd);
|
||||
|
||||
/**
|
||||
* Reconnect the given context using the saved information.
|
||||
@@ -262,7 +167,7 @@ int redisReconnect(redisContext *c);
|
||||
int redisSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisEnableKeepAlive(redisContext *c);
|
||||
void redisFree(redisContext *c);
|
||||
redisFD redisFreeKeepFd(redisContext *c);
|
||||
int redisFreeKeepFd(redisContext *c);
|
||||
int redisBufferRead(redisContext *c);
|
||||
int redisBufferWrite(redisContext *c, int *done);
|
||||
|
||||
|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
Name: hiredis
|
||||
Description: Minimalistic C client library for Redis.
|
||||
Version: @PROJECT_VERSION@
|
||||
Libs: -L${libdir} -lhiredis
|
||||
Cflags: -I${pkgincludedir} -D_FILE_OFFSET_BITS=64
|
||||
Vendored
-53
@@ -1,53 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_SSL_H
|
||||
#define __HIREDIS_SSL_H
|
||||
|
||||
/* This is the underlying struct for SSL in ssl.h, which is not included to
|
||||
* keep build dependencies short here.
|
||||
*/
|
||||
struct ssl_st;
|
||||
|
||||
/**
|
||||
* Secure the connection using SSL. This should be done before any command is
|
||||
* executed on the connection.
|
||||
*/
|
||||
int redisSecureConnection(redisContext *c, const char *capath, const char *certpath,
|
||||
const char *keypath, const char *servername);
|
||||
|
||||
/**
|
||||
* Initiate SSL/TLS negotiation on a provided context.
|
||||
*/
|
||||
|
||||
int redisInitiateSSL(redisContext *c, struct ssl_st *ssl);
|
||||
|
||||
#endif /* __HIREDIS_SSL_H */
|
||||
Vendored
-12
@@ -1,12 +0,0 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
includedir=${prefix}/include
|
||||
pkgincludedir=${includedir}/hiredis
|
||||
|
||||
Name: hiredis_ssl
|
||||
Description: SSL Support for hiredis.
|
||||
Version: @PROJECT_VERSION@
|
||||
Requires: hiredis
|
||||
Libs: -L${libdir} -lhiredis_ssl
|
||||
Libs.private: -lssl -lcrypto
|
||||
Vendored
+35
-87
@@ -34,64 +34,36 @@
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "net.h"
|
||||
#include "sds.h"
|
||||
#include "sockcompat.h"
|
||||
#include "win32.h"
|
||||
|
||||
/* Defined in hiredis.c */
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
void redisNetClose(redisContext *c) {
|
||||
if (c && c->fd != REDIS_INVALID_FD) {
|
||||
static void redisContextCloseFd(redisContext *c) {
|
||||
if (c && c->fd >= 0) {
|
||||
close(c->fd);
|
||||
c->fd = REDIS_INVALID_FD;
|
||||
c->fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
int nread = recv(c->fd, buf, bufcap, 0);
|
||||
if (nread == -1) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
return 0;
|
||||
} else if(errno == ETIMEDOUT && (c->flags & REDIS_BLOCK)) {
|
||||
/* especially in windows */
|
||||
__redisSetError(c, REDIS_ERR_TIMEOUT, "recv timeout");
|
||||
return -1;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
} else if (nread == 0) {
|
||||
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
||||
return -1;
|
||||
} else {
|
||||
return nread;
|
||||
}
|
||||
}
|
||||
|
||||
int redisNetWrite(redisContext *c) {
|
||||
int nwritten = send(c->fd, c->obuf, sdslen(c->obuf), 0);
|
||||
if (nwritten < 0) {
|
||||
if ((errno == EWOULDBLOCK && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) {
|
||||
/* Try again later */
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
static void __redisSetErrorFromErrno(redisContext *c, int type, const char *prefix) {
|
||||
int errorno = errno; /* snprintf() may change errno */
|
||||
char buf[128] = { 0 };
|
||||
@@ -107,15 +79,15 @@ static int redisSetReuseAddr(redisContext *c) {
|
||||
int on = 1;
|
||||
if (setsockopt(c->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
static int redisCreateSocket(redisContext *c, int type) {
|
||||
redisFD s;
|
||||
if ((s = socket(type, SOCK_STREAM, 0)) == REDIS_INVALID_FD) {
|
||||
int s;
|
||||
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
@@ -129,7 +101,6 @@ static int redisCreateSocket(redisContext *c, int type) {
|
||||
}
|
||||
|
||||
static int redisSetBlocking(redisContext *c, int blocking) {
|
||||
#ifndef _WIN32
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
@@ -137,7 +108,7 @@ static int redisSetBlocking(redisContext *c, int blocking) {
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(c->fd, F_GETFL)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_GETFL)");
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
@@ -148,23 +119,15 @@ static int redisSetBlocking(redisContext *c, int blocking) {
|
||||
|
||||
if (fcntl(c->fd, F_SETFL, flags) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"fcntl(F_SETFL)");
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#else
|
||||
u_long mode = blocking ? 0 : 1;
|
||||
if (ioctl(c->fd, FIONBIO, &mode) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "ioctl(FIONBIO)");
|
||||
redisNetClose(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
int redisKeepAlive(redisContext *c, int interval) {
|
||||
int val = 1;
|
||||
redisFD fd = c->fd;
|
||||
int fd = c->fd;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1){
|
||||
__redisSetError(c,REDIS_ERR_OTHER,strerror(errno));
|
||||
@@ -207,7 +170,7 @@ static int redisSetTcpNoDelay(redisContext *c) {
|
||||
int yes = 1;
|
||||
if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(TCP_NODELAY)");
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
return REDIS_OK;
|
||||
@@ -249,12 +212,12 @@ static int redisContextWaitReady(redisContext *c, long msec) {
|
||||
|
||||
if ((res = poll(wfd, 1, msec)) == -1) {
|
||||
__redisSetErrorFromErrno(c, REDIS_ERR_IO, "poll(2)");
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
} else if (res == 0) {
|
||||
errno = ETIMEDOUT;
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
@@ -267,7 +230,7 @@ static int redisContextWaitReady(redisContext *c, long msec) {
|
||||
}
|
||||
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,NULL);
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
@@ -314,18 +277,11 @@ int redisCheckSocketError(redisContext *c) {
|
||||
}
|
||||
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
const void *to_ptr = &tv;
|
||||
size_t to_sz = sizeof(tv);
|
||||
#ifdef _WIN32
|
||||
DWORD timeout_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
to_ptr = &timeout_msec;
|
||||
to_sz = sizeof(timeout_msec);
|
||||
#endif
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,to_ptr,to_sz) == -1) {
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_RCVTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,to_ptr,to_sz) == -1) {
|
||||
if (setsockopt(c->fd,SOL_SOCKET,SO_SNDTIMEO,&tv,sizeof(tv)) == -1) {
|
||||
__redisSetErrorFromErrno(c,REDIS_ERR_IO,"setsockopt(SO_SNDTIMEO)");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
@@ -335,8 +291,7 @@ int redisContextSetTimeout(redisContext *c, const struct timeval tv) {
|
||||
static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
const struct timeval *timeout,
|
||||
const char *source_addr) {
|
||||
redisFD s;
|
||||
int rv, n;
|
||||
int s, rv, n;
|
||||
char _port[6]; /* strlen("65535"); */
|
||||
struct addrinfo hints, *servinfo, *bservinfo, *p, *b;
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
@@ -405,7 +360,7 @@ static int _redisContextConnectTcp(redisContext *c, const char *addr, int port,
|
||||
}
|
||||
for (p = servinfo; p != NULL; p = p->ai_next) {
|
||||
addrretry:
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == REDIS_INVALID_FD)
|
||||
if ((s = socket(p->ai_family,p->ai_socktype,p->ai_protocol)) == -1)
|
||||
continue;
|
||||
|
||||
c->fd = s;
|
||||
@@ -446,14 +401,16 @@ addrretry:
|
||||
}
|
||||
|
||||
/* For repeat connection */
|
||||
free(c->saddr);
|
||||
if (c->saddr) {
|
||||
free(c->saddr);
|
||||
}
|
||||
c->saddr = malloc(p->ai_addrlen);
|
||||
memcpy(c->saddr, p->ai_addr, p->ai_addrlen);
|
||||
c->addrlen = p->ai_addrlen;
|
||||
|
||||
if (connect(s,p->ai_addr,p->ai_addrlen) == -1) {
|
||||
if (errno == EHOSTUNREACH) {
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
continue;
|
||||
} else if (errno == EINPROGRESS) {
|
||||
if (blocking) {
|
||||
@@ -467,7 +424,7 @@ addrretry:
|
||||
if (++reuses >= REDIS_CONNECT_RETRIES) {
|
||||
goto error;
|
||||
} else {
|
||||
redisNetClose(c);
|
||||
redisContextCloseFd(c);
|
||||
goto addrretry;
|
||||
}
|
||||
} else {
|
||||
@@ -514,9 +471,8 @@ int redisContextConnectBindTcp(redisContext *c, const char *addr, int port,
|
||||
}
|
||||
|
||||
int redisContextConnectUnix(redisContext *c, const char *path, const struct timeval *timeout) {
|
||||
#ifndef _WIN32
|
||||
int blocking = (c->flags & REDIS_BLOCK);
|
||||
struct sockaddr_un *sa;
|
||||
struct sockaddr_un sa;
|
||||
long timeout_msec = -1;
|
||||
|
||||
if (redisCreateSocket(c,AF_UNIX) < 0)
|
||||
@@ -543,11 +499,9 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
|
||||
if (redisContextTimeoutMsec(c,&timeout_msec) != REDIS_OK)
|
||||
return REDIS_ERR;
|
||||
|
||||
sa = (struct sockaddr_un*)(c->saddr = malloc(sizeof(struct sockaddr_un)));
|
||||
c->addrlen = sizeof(struct sockaddr_un);
|
||||
sa->sun_family = AF_UNIX;
|
||||
strncpy(sa->sun_path, path, sizeof(sa->sun_path) - 1);
|
||||
if (connect(c->fd, (struct sockaddr*)sa, sizeof(*sa)) == -1) {
|
||||
sa.sun_family = AF_UNIX;
|
||||
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
|
||||
if (connect(c->fd, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
|
||||
if (errno == EINPROGRESS && !blocking) {
|
||||
/* This is ok. */
|
||||
} else {
|
||||
@@ -562,10 +516,4 @@ int redisContextConnectUnix(redisContext *c, const char *path, const struct time
|
||||
|
||||
c->flags |= REDIS_CONNECTED;
|
||||
return REDIS_OK;
|
||||
#else
|
||||
/* We currently do not support Unix sockets for Windows. */
|
||||
/* TODO(m): https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/ */
|
||||
errno = EPROTONOSUPPORT;
|
||||
return REDIS_ERR;
|
||||
#endif /* _WIN32 */
|
||||
}
|
||||
Vendored
-4
@@ -37,10 +37,6 @@
|
||||
|
||||
#include "hiredis.h"
|
||||
|
||||
void redisNetClose(redisContext *c);
|
||||
int redisNetRead(redisContext *c, char *buf, size_t bufcap);
|
||||
int redisNetWrite(redisContext *c);
|
||||
|
||||
int redisCheckSocketError(redisContext *c);
|
||||
int redisContextSetTimeout(redisContext *c, const struct timeval tv);
|
||||
int redisContextConnectTcp(redisContext *c, const char *addr, int port, const struct timeval *timeout);
|
||||
|
||||
Vendored
+5
-22
@@ -34,7 +34,6 @@
|
||||
#include <stdlib.h>
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
@@ -44,7 +43,6 @@
|
||||
|
||||
#include "read.h"
|
||||
#include "sds.h"
|
||||
#include "win32.h"
|
||||
|
||||
static void __redisReaderSetError(redisReader *r, int type, const char *str) {
|
||||
size_t len;
|
||||
@@ -295,9 +293,9 @@ static int processLineItem(redisReader *r) {
|
||||
buf[len] = '\0';
|
||||
|
||||
if (strcasecmp(buf,",inf") == 0) {
|
||||
d = INFINITY; /* Positive infinite. */
|
||||
d = 1.0/0.0; /* Positive infinite. */
|
||||
} else if (strcasecmp(buf,",-inf") == 0) {
|
||||
d = -INFINITY; /* Nevative infinite. */
|
||||
d = -1.0/0.0; /* Nevative infinite. */
|
||||
} else {
|
||||
d = strtod((char*)buf,&eptr);
|
||||
if (buf[0] == '\0' || eptr[0] != '\0' || isnan(d)) {
|
||||
@@ -380,18 +378,10 @@ static int processBulkItem(redisReader *r) {
|
||||
/* Only continue when the buffer contains the entire bulk item. */
|
||||
bytelen += len+2; /* include \r\n */
|
||||
if (r->pos+bytelen <= r->len) {
|
||||
if ((cur->type == REDIS_REPLY_VERB && len < 4) ||
|
||||
(cur->type == REDIS_REPLY_VERB && s[5] != ':'))
|
||||
{
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Verbatim string 4 bytes of content type are "
|
||||
"missing or incorrectly encoded.");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
if (r->fn && r->fn->createString)
|
||||
obj = r->fn->createString(cur,s+2,len);
|
||||
else
|
||||
obj = (void*)(long)cur->type;
|
||||
obj = (void*)REDIS_REPLY_STRING;
|
||||
success = 1;
|
||||
}
|
||||
}
|
||||
@@ -439,7 +429,7 @@ static int processAggregateItem(redisReader *r) {
|
||||
|
||||
root = (r->ridx == 0);
|
||||
|
||||
if (elements < -1 || (LLONG_MAX > SIZE_MAX && elements > SIZE_MAX)) {
|
||||
if (elements < -1 || elements > INT_MAX) {
|
||||
__redisReaderSetError(r,REDIS_ERR_PROTOCOL,
|
||||
"Multi-bulk length out of range");
|
||||
return REDIS_ERR;
|
||||
@@ -532,9 +522,6 @@ static int processItem(redisReader *r) {
|
||||
case '#':
|
||||
cur->type = REDIS_REPLY_BOOL;
|
||||
break;
|
||||
case '=':
|
||||
cur->type = REDIS_REPLY_VERB;
|
||||
break;
|
||||
default:
|
||||
__redisReaderSetErrorProtocolByte(r,*p);
|
||||
return REDIS_ERR;
|
||||
@@ -555,7 +542,6 @@ static int processItem(redisReader *r) {
|
||||
case REDIS_REPLY_BOOL:
|
||||
return processLineItem(r);
|
||||
case REDIS_REPLY_STRING:
|
||||
case REDIS_REPLY_VERB:
|
||||
return processBulkItem(r);
|
||||
case REDIS_REPLY_ARRAY:
|
||||
case REDIS_REPLY_MAP:
|
||||
@@ -670,11 +656,8 @@ int redisReaderGetReply(redisReader *r, void **reply) {
|
||||
|
||||
/* Emit a reply when there is one. */
|
||||
if (r->ridx == -1) {
|
||||
if (reply != NULL) {
|
||||
if (reply != NULL)
|
||||
*reply = r->reply;
|
||||
} else if (r->reply != NULL && r->fn && r->fn->freeObject) {
|
||||
r->fn->freeObject(r->reply);
|
||||
}
|
||||
r->reply = NULL;
|
||||
}
|
||||
return REDIS_OK;
|
||||
|
||||
Vendored
+2
-3
@@ -45,7 +45,6 @@
|
||||
#define REDIS_ERR_EOF 3 /* End of file */
|
||||
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
|
||||
#define REDIS_ERR_OOM 5 /* Out of memory */
|
||||
#define REDIS_ERR_TIMEOUT 6 /* Timed out */
|
||||
#define REDIS_ERR_OTHER 2 /* Everything else... */
|
||||
|
||||
#define REDIS_REPLY_STRING 1
|
||||
@@ -56,12 +55,12 @@
|
||||
#define REDIS_REPLY_ERROR 6
|
||||
#define REDIS_REPLY_DOUBLE 7
|
||||
#define REDIS_REPLY_BOOL 8
|
||||
#define REDIS_REPLY_VERB 9
|
||||
#define REDIS_REPLY_MAP 9
|
||||
#define REDIS_REPLY_SET 10
|
||||
#define REDIS_REPLY_ATTR 11
|
||||
#define REDIS_REPLY_PUSH 12
|
||||
#define REDIS_REPLY_BIGNUM 13
|
||||
#define REDIS_REPLY_VERB 14
|
||||
|
||||
#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
|
||||
|
||||
@@ -80,7 +79,7 @@ typedef struct redisReadTask {
|
||||
|
||||
typedef struct redisReplyObjectFunctions {
|
||||
void *(*createString)(const redisReadTask*, char*, size_t);
|
||||
void *(*createArray)(const redisReadTask*, size_t);
|
||||
void *(*createArray)(const redisReadTask*, int);
|
||||
void *(*createInteger)(const redisReadTask*, long long);
|
||||
void *(*createDouble)(const redisReadTask*, double, char*, size_t);
|
||||
void *(*createNil)(const redisReadTask*);
|
||||
|
||||
Vendored
+1
-1
@@ -1035,7 +1035,7 @@ sds *sdssplitargs(const char *line, int *argc) {
|
||||
s_free(vector);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
vector = new_vector;
|
||||
vector[*argc] = current;
|
||||
(*argc)++;
|
||||
|
||||
Vendored
+14
-27
@@ -34,9 +34,6 @@
|
||||
#define __SDS_H
|
||||
|
||||
#define SDS_MAX_PREALLOC (1024*1024)
|
||||
#ifdef _MSC_VER
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdarg.h>
|
||||
@@ -48,41 +45,31 @@ typedef char *sds;
|
||||
* However is here to document the layout of type 5 SDS strings. */
|
||||
struct __attribute__ ((__packed__)) sdshdr5 {
|
||||
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
|
||||
#ifndef __cplusplus
|
||||
char buf[];
|
||||
#endif
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr8 {
|
||||
uint8_t len; /* used */
|
||||
uint8_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
#ifndef __cplusplus
|
||||
char buf[];
|
||||
#endif
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr16 {
|
||||
uint16_t len; /* used */
|
||||
uint16_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
#ifndef __cplusplus
|
||||
char buf[];
|
||||
#endif
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr32 {
|
||||
uint32_t len; /* used */
|
||||
uint32_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
#ifndef __cplusplus
|
||||
char buf[];
|
||||
#endif
|
||||
};
|
||||
struct __attribute__ ((__packed__)) sdshdr64 {
|
||||
uint64_t len; /* used */
|
||||
uint64_t alloc; /* excluding the header and null terminator */
|
||||
unsigned char flags; /* 3 lsb of type, 5 unused bits */
|
||||
#ifndef __cplusplus
|
||||
char buf[];
|
||||
#endif
|
||||
};
|
||||
|
||||
#define SDS_TYPE_5 0
|
||||
@@ -146,20 +133,20 @@ static inline void sdssetlen(sds s, size_t newlen) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
*fp = (unsigned char)(SDS_TYPE_5 | (newlen << SDS_TYPE_BITS));
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len = (uint8_t)newlen;
|
||||
SDS_HDR(8,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len = (uint16_t)newlen;
|
||||
SDS_HDR(16,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len = (uint32_t)newlen;
|
||||
SDS_HDR(32,s)->len = newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len = (uint64_t)newlen;
|
||||
SDS_HDR(64,s)->len = newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -170,21 +157,21 @@ static inline void sdsinclen(sds s, size_t inc) {
|
||||
case SDS_TYPE_5:
|
||||
{
|
||||
unsigned char *fp = ((unsigned char*)s)-1;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+(unsigned char)inc;
|
||||
unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
|
||||
*fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
|
||||
}
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->len += (uint8_t)inc;
|
||||
SDS_HDR(8,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->len += (uint16_t)inc;
|
||||
SDS_HDR(16,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->len += (uint32_t)inc;
|
||||
SDS_HDR(32,s)->len += inc;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->len += (uint64_t)inc;
|
||||
SDS_HDR(64,s)->len += inc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -214,16 +201,16 @@ static inline void sdssetalloc(sds s, size_t newlen) {
|
||||
/* Nothing to do, this type has no total allocation info. */
|
||||
break;
|
||||
case SDS_TYPE_8:
|
||||
SDS_HDR(8,s)->alloc = (uint8_t)newlen;
|
||||
SDS_HDR(8,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_16:
|
||||
SDS_HDR(16,s)->alloc = (uint16_t)newlen;
|
||||
SDS_HDR(16,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_32:
|
||||
SDS_HDR(32,s)->alloc = (uint32_t)newlen;
|
||||
SDS_HDR(32,s)->alloc = newlen;
|
||||
break;
|
||||
case SDS_TYPE_64:
|
||||
SDS_HDR(64,s)->alloc = (uint64_t)newlen;
|
||||
SDS_HDR(64,s)->alloc = newlen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
-248
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define REDIS_SOCKCOMPAT_IMPLEMENTATION
|
||||
#include "sockcompat.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
static int _wsaErrorToErrno(int err) {
|
||||
switch (err) {
|
||||
case WSAEWOULDBLOCK:
|
||||
return EWOULDBLOCK;
|
||||
case WSAEINPROGRESS:
|
||||
return EINPROGRESS;
|
||||
case WSAEALREADY:
|
||||
return EALREADY;
|
||||
case WSAENOTSOCK:
|
||||
return ENOTSOCK;
|
||||
case WSAEDESTADDRREQ:
|
||||
return EDESTADDRREQ;
|
||||
case WSAEMSGSIZE:
|
||||
return EMSGSIZE;
|
||||
case WSAEPROTOTYPE:
|
||||
return EPROTOTYPE;
|
||||
case WSAENOPROTOOPT:
|
||||
return ENOPROTOOPT;
|
||||
case WSAEPROTONOSUPPORT:
|
||||
return EPROTONOSUPPORT;
|
||||
case WSAEOPNOTSUPP:
|
||||
return EOPNOTSUPP;
|
||||
case WSAEAFNOSUPPORT:
|
||||
return EAFNOSUPPORT;
|
||||
case WSAEADDRINUSE:
|
||||
return EADDRINUSE;
|
||||
case WSAEADDRNOTAVAIL:
|
||||
return EADDRNOTAVAIL;
|
||||
case WSAENETDOWN:
|
||||
return ENETDOWN;
|
||||
case WSAENETUNREACH:
|
||||
return ENETUNREACH;
|
||||
case WSAENETRESET:
|
||||
return ENETRESET;
|
||||
case WSAECONNABORTED:
|
||||
return ECONNABORTED;
|
||||
case WSAECONNRESET:
|
||||
return ECONNRESET;
|
||||
case WSAENOBUFS:
|
||||
return ENOBUFS;
|
||||
case WSAEISCONN:
|
||||
return EISCONN;
|
||||
case WSAENOTCONN:
|
||||
return ENOTCONN;
|
||||
case WSAETIMEDOUT:
|
||||
return ETIMEDOUT;
|
||||
case WSAECONNREFUSED:
|
||||
return ECONNREFUSED;
|
||||
case WSAELOOP:
|
||||
return ELOOP;
|
||||
case WSAENAMETOOLONG:
|
||||
return ENAMETOOLONG;
|
||||
case WSAEHOSTUNREACH:
|
||||
return EHOSTUNREACH;
|
||||
case WSAENOTEMPTY:
|
||||
return ENOTEMPTY;
|
||||
default:
|
||||
/* We just return a generic I/O error if we could not find a relevant error. */
|
||||
return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static void _updateErrno(int success) {
|
||||
errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError());
|
||||
}
|
||||
|
||||
static int _initWinsock() {
|
||||
static int s_initialized = 0;
|
||||
if (!s_initialized) {
|
||||
static WSADATA wsadata;
|
||||
int err = WSAStartup(MAKEWORD(2,2), &wsadata);
|
||||
if (err != 0) {
|
||||
errno = _wsaErrorToErrno(err);
|
||||
return 0;
|
||||
}
|
||||
s_initialized = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
|
||||
/* Note: This function is likely to be called before other functions, so run init here. */
|
||||
if (!_initWinsock()) {
|
||||
return EAI_FAIL;
|
||||
}
|
||||
|
||||
switch (getaddrinfo(node, service, hints, res)) {
|
||||
case 0: return 0;
|
||||
case WSATRY_AGAIN: return EAI_AGAIN;
|
||||
case WSAEINVAL: return EAI_BADFLAGS;
|
||||
case WSAEAFNOSUPPORT: return EAI_FAMILY;
|
||||
case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY;
|
||||
case WSAHOST_NOT_FOUND: return EAI_NONAME;
|
||||
case WSATYPE_NOT_FOUND: return EAI_SERVICE;
|
||||
case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE;
|
||||
default: return EAI_FAIL; /* Including WSANO_RECOVERY */
|
||||
}
|
||||
}
|
||||
|
||||
const char *win32_gai_strerror(int errcode) {
|
||||
switch (errcode) {
|
||||
case 0: errcode = 0; break;
|
||||
case EAI_AGAIN: errcode = WSATRY_AGAIN; break;
|
||||
case EAI_BADFLAGS: errcode = WSAEINVAL; break;
|
||||
case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break;
|
||||
case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break;
|
||||
case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break;
|
||||
case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break;
|
||||
case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break;
|
||||
default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */
|
||||
}
|
||||
return gai_strerror(errcode);
|
||||
}
|
||||
|
||||
void win32_freeaddrinfo(struct addrinfo *res) {
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
SOCKET win32_socket(int domain, int type, int protocol) {
|
||||
SOCKET s;
|
||||
|
||||
/* Note: This function is likely to be called before other functions, so run init here. */
|
||||
if (!_initWinsock()) {
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
_updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET);
|
||||
return s;
|
||||
}
|
||||
|
||||
int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) {
|
||||
int ret = ioctlsocket(fd, (long)request, argp);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = bind(sockfd, addr, addrlen);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
||||
int ret = connect(sockfd, addr, addrlen);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
|
||||
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
|
||||
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
|
||||
* logic consistent. */
|
||||
if (errno == EWOULDBLOCK) {
|
||||
errno = EINPROGRESS;
|
||||
}
|
||||
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) {
|
||||
int ret = 0;
|
||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
||||
if (*optlen >= sizeof (struct timeval)) {
|
||||
struct timeval *tv = optval;
|
||||
DWORD timeout = 0;
|
||||
socklen_t dwlen = 0;
|
||||
ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen);
|
||||
tv->tv_sec = timeout / 1000;
|
||||
tv->tv_usec = (timeout * 1000) % 1000000;
|
||||
} else {
|
||||
ret = WSAEFAULT;
|
||||
}
|
||||
*optlen = sizeof (struct timeval);
|
||||
} else {
|
||||
ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);
|
||||
}
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {
|
||||
int ret = 0;
|
||||
if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
|
||||
struct timeval *tv = optval;
|
||||
DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
||||
ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));
|
||||
} else {
|
||||
ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen);
|
||||
}
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_close(SOCKET fd) {
|
||||
int ret = closesocket(fd);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) {
|
||||
int ret = recv(sockfd, (char*)buf, (int)len, flags);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) {
|
||||
int ret = send(sockfd, (const char*)buf, (int)len, flags);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
|
||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
|
||||
int ret = WSAPoll(fds, nfds, timeout);
|
||||
_updateErrno(ret != SOCKET_ERROR);
|
||||
return ret != SOCKET_ERROR ? ret : -1;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
Vendored
-91
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __SOCKCOMPAT_H
|
||||
#define __SOCKCOMPAT_H
|
||||
|
||||
#ifndef _WIN32
|
||||
/* For POSIX systems we use the standard BSD socket API. */
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <poll.h>
|
||||
#else
|
||||
/* For Windows we use winsock. */
|
||||
#undef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600 /* To get WSAPoll etc. */
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef signed long ssize_t;
|
||||
#endif
|
||||
|
||||
/* Emulate the parts of the BSD socket API that we need (override the winsock signatures). */
|
||||
int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
|
||||
const char *win32_gai_strerror(int errcode);
|
||||
void win32_freeaddrinfo(struct addrinfo *res);
|
||||
SOCKET win32_socket(int domain, int type, int protocol);
|
||||
int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp);
|
||||
int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen);
|
||||
int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
||||
int win32_close(SOCKET fd);
|
||||
ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags);
|
||||
ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
|
||||
typedef ULONG nfds_t;
|
||||
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
|
||||
|
||||
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
|
||||
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
|
||||
#undef gai_strerror
|
||||
#define gai_strerror(errcode) win32_gai_strerror(errcode)
|
||||
#define freeaddrinfo(res) win32_freeaddrinfo(res)
|
||||
#define socket(domain, type, protocol) win32_socket(domain, type, protocol)
|
||||
#define ioctl(fd, request, argp) win32_ioctl(fd, request, argp)
|
||||
#define bind(sockfd, addr, addrlen) win32_bind(sockfd, addr, addrlen)
|
||||
#define connect(sockfd, addr, addrlen) win32_connect(sockfd, addr, addrlen)
|
||||
#define getsockopt(sockfd, level, optname, optval, optlen) win32_getsockopt(sockfd, level, optname, optval, optlen)
|
||||
#define setsockopt(sockfd, level, optname, optval, optlen) win32_setsockopt(sockfd, level, optname, optval, optlen)
|
||||
#define close(fd) win32_close(fd)
|
||||
#define recv(sockfd, buf, len, flags) win32_recv(sockfd, buf, len, flags)
|
||||
#define send(sockfd, buf, len, flags) win32_send(sockfd, buf, len, flags)
|
||||
#define poll(fds, nfds, timeout) win32_poll(fds, nfds, timeout)
|
||||
#endif /* REDIS_SOCKCOMPAT_IMPLEMENTATION */
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* __SOCKCOMPAT_H */
|
||||
Vendored
-448
@@ -1,448 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "async.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "async_private.h"
|
||||
|
||||
void __redisSetError(redisContext *c, int type, const char *str);
|
||||
|
||||
/* The SSL context is attached to SSL/TLS connections as a privdata. */
|
||||
typedef struct redisSSLContext {
|
||||
/**
|
||||
* OpenSSL SSL_CTX; It is optional and will not be set when using
|
||||
* user-supplied SSL.
|
||||
*/
|
||||
SSL_CTX *ssl_ctx;
|
||||
|
||||
/**
|
||||
* OpenSSL SSL object.
|
||||
*/
|
||||
SSL *ssl;
|
||||
|
||||
/**
|
||||
* SSL_write() requires to be called again with the same arguments it was
|
||||
* previously called with in the event of an SSL_read/SSL_write situation
|
||||
*/
|
||||
size_t lastLen;
|
||||
|
||||
/** Whether the SSL layer requires read (possibly before a write) */
|
||||
int wantRead;
|
||||
|
||||
/**
|
||||
* Whether a write was requested prior to a read. If set, the write()
|
||||
* should resume whenever a read takes place, if possible
|
||||
*/
|
||||
int pendingWrite;
|
||||
} redisSSLContext;
|
||||
|
||||
/* Forward declaration */
|
||||
redisContextFuncs redisContextSSLFuncs;
|
||||
|
||||
#ifdef HIREDIS_SSL_TRACE
|
||||
/**
|
||||
* Callback used for debugging
|
||||
*/
|
||||
static void sslLogCallback(const SSL *ssl, int where, int ret) {
|
||||
const char *retstr = "";
|
||||
int should_log = 1;
|
||||
/* Ignore low-level SSL stuff */
|
||||
|
||||
if (where & SSL_CB_ALERT) {
|
||||
should_log = 1;
|
||||
}
|
||||
if (where == SSL_CB_HANDSHAKE_START || where == SSL_CB_HANDSHAKE_DONE) {
|
||||
should_log = 1;
|
||||
}
|
||||
if ((where & SSL_CB_EXIT) && ret == 0) {
|
||||
should_log = 1;
|
||||
}
|
||||
|
||||
if (!should_log) {
|
||||
return;
|
||||
}
|
||||
|
||||
retstr = SSL_alert_type_string(ret);
|
||||
printf("ST(0x%x). %s. R(0x%x)%s\n", where, SSL_state_string_long(ssl), ret, retstr);
|
||||
|
||||
if (where == SSL_CB_HANDSHAKE_DONE) {
|
||||
printf("Using SSL version %s. Cipher=%s\n", SSL_get_version(ssl), SSL_get_cipher_name(ssl));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* OpenSSL global initialization and locking handling callbacks.
|
||||
* Note that this is only required for OpenSSL < 1.1.0.
|
||||
*/
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
#define HIREDIS_USE_CRYPTO_LOCKS
|
||||
#endif
|
||||
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
typedef pthread_mutex_t sslLockType;
|
||||
static void sslLockInit(sslLockType *l) {
|
||||
pthread_mutex_init(l, NULL);
|
||||
}
|
||||
static void sslLockAcquire(sslLockType *l) {
|
||||
pthread_mutex_lock(l);
|
||||
}
|
||||
static void sslLockRelease(sslLockType *l) {
|
||||
pthread_mutex_unlock(l);
|
||||
}
|
||||
static pthread_mutex_t *ossl_locks;
|
||||
|
||||
static void opensslDoLock(int mode, int lkid, const char *f, int line) {
|
||||
sslLockType *l = ossl_locks + lkid;
|
||||
|
||||
if (mode & CRYPTO_LOCK) {
|
||||
sslLockAcquire(l);
|
||||
} else {
|
||||
sslLockRelease(l);
|
||||
}
|
||||
|
||||
(void)f;
|
||||
(void)line;
|
||||
}
|
||||
|
||||
static void initOpensslLocks(void) {
|
||||
unsigned ii, nlocks;
|
||||
if (CRYPTO_get_locking_callback() != NULL) {
|
||||
/* Someone already set the callback before us. Don't destroy it! */
|
||||
return;
|
||||
}
|
||||
nlocks = CRYPTO_num_locks();
|
||||
ossl_locks = malloc(sizeof(*ossl_locks) * nlocks);
|
||||
for (ii = 0; ii < nlocks; ii++) {
|
||||
sslLockInit(ossl_locks + ii);
|
||||
}
|
||||
CRYPTO_set_locking_callback(opensslDoLock);
|
||||
}
|
||||
#endif /* HIREDIS_USE_CRYPTO_LOCKS */
|
||||
|
||||
/**
|
||||
* SSL Connection initialization.
|
||||
*/
|
||||
|
||||
static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
|
||||
if (c->privdata) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
|
||||
return REDIS_ERR;
|
||||
}
|
||||
c->privdata = calloc(1, sizeof(redisSSLContext));
|
||||
|
||||
c->funcs = &redisContextSSLFuncs;
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
|
||||
rssl->ssl_ctx = ssl_ctx;
|
||||
rssl->ssl = ssl;
|
||||
|
||||
SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
SSL_set_fd(rssl->ssl, c->fd);
|
||||
SSL_set_connect_state(rssl->ssl);
|
||||
|
||||
ERR_clear_error();
|
||||
int rv = SSL_connect(rssl->ssl);
|
||||
if (rv == 1) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
rv = SSL_get_error(rssl->ssl, rv);
|
||||
if (((c->flags & REDIS_BLOCK) == 0) &&
|
||||
(rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
|
||||
return REDIS_OK;
|
||||
}
|
||||
|
||||
if (c->err == 0) {
|
||||
char err[512];
|
||||
if (rv == SSL_ERROR_SYSCALL)
|
||||
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
|
||||
else {
|
||||
unsigned long e = ERR_peek_last_error();
|
||||
snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
|
||||
ERR_reason_error_string(e));
|
||||
}
|
||||
__redisSetError(c, REDIS_ERR_IO, err);
|
||||
}
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
int redisInitiateSSL(redisContext *c, SSL *ssl) {
|
||||
return redisSSLConnect(c, NULL, ssl);
|
||||
}
|
||||
|
||||
int redisSecureConnection(redisContext *c, const char *capath,
|
||||
const char *certpath, const char *keypath, const char *servername) {
|
||||
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
SSL *ssl = NULL;
|
||||
|
||||
/* Initialize global OpenSSL stuff */
|
||||
static int isInit = 0;
|
||||
if (!isInit) {
|
||||
isInit = 1;
|
||||
SSL_library_init();
|
||||
#ifdef HIREDIS_USE_CRYPTO_LOCKS
|
||||
initOpensslLocks();
|
||||
#endif
|
||||
}
|
||||
|
||||
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
if (!ssl_ctx) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Failed to create SSL_CTX");
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef HIREDIS_SSL_TRACE
|
||||
SSL_CTX_set_info_callback(ssl_ctx, sslLogCallback);
|
||||
#endif
|
||||
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
if ((certpath != NULL && keypath == NULL) || (keypath != NULL && certpath == NULL)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "certpath and keypath must be specified together");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (capath) {
|
||||
if (!SSL_CTX_load_verify_locations(ssl_ctx, capath, NULL)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid CA certificate");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (certpath) {
|
||||
if (!SSL_CTX_use_certificate_chain_file(ssl_ctx, certpath)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client certificate");
|
||||
goto error;
|
||||
}
|
||||
if (!SSL_CTX_use_PrivateKey_file(ssl_ctx, keypath, SSL_FILETYPE_PEM)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Invalid client key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
ssl = SSL_new(ssl_ctx);
|
||||
if (!ssl) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
|
||||
goto error;
|
||||
}
|
||||
if (servername) {
|
||||
if (!SSL_set_tlsext_host_name(ssl, servername)) {
|
||||
__redisSetError(c, REDIS_ERR_OTHER, "Couldn't set server name indication");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return redisSSLConnect(c, ssl_ctx, ssl);
|
||||
|
||||
error:
|
||||
if (ssl) SSL_free(ssl);
|
||||
if (ssl_ctx) SSL_CTX_free(ssl_ctx);
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
static int maybeCheckWant(redisSSLContext *rssl, int rv) {
|
||||
/**
|
||||
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
|
||||
* and true is returned. False is returned otherwise
|
||||
*/
|
||||
if (rv == SSL_ERROR_WANT_READ) {
|
||||
rssl->wantRead = 1;
|
||||
return 1;
|
||||
} else if (rv == SSL_ERROR_WANT_WRITE) {
|
||||
rssl->pendingWrite = 1;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of redisContextFuncs for SSL connections.
|
||||
*/
|
||||
|
||||
static void redisSSLFreeContext(void *privdata){
|
||||
redisSSLContext *rsc = privdata;
|
||||
|
||||
if (!rsc) return;
|
||||
if (rsc->ssl) {
|
||||
SSL_free(rsc->ssl);
|
||||
rsc->ssl = NULL;
|
||||
}
|
||||
if (rsc->ssl_ctx) {
|
||||
SSL_CTX_free(rsc->ssl_ctx);
|
||||
rsc->ssl_ctx = NULL;
|
||||
}
|
||||
free(rsc);
|
||||
}
|
||||
|
||||
static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
|
||||
int nread = SSL_read(rssl->ssl, buf, bufcap);
|
||||
if (nread > 0) {
|
||||
return nread;
|
||||
} else if (nread == 0) {
|
||||
__redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
|
||||
return -1;
|
||||
} else {
|
||||
int err = SSL_get_error(rssl->ssl, nread);
|
||||
if (c->flags & REDIS_BLOCK) {
|
||||
/**
|
||||
* In blocking mode, we should never end up in a situation where
|
||||
* we get an error without it being an actual error, except
|
||||
* in the case of EINTR, which can be spuriously received from
|
||||
* debuggers or whatever.
|
||||
*/
|
||||
if (errno == EINTR) {
|
||||
return 0;
|
||||
} else {
|
||||
const char *msg = NULL;
|
||||
if (errno == EAGAIN) {
|
||||
msg = "Resource temporarily unavailable";
|
||||
}
|
||||
__redisSetError(c, REDIS_ERR_IO, msg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We can very well get an EWOULDBLOCK/EAGAIN, however
|
||||
*/
|
||||
if (maybeCheckWant(rssl, err)) {
|
||||
return 0;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int redisSSLWrite(redisContext *c) {
|
||||
redisSSLContext *rssl = c->privdata;
|
||||
|
||||
size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
|
||||
int rv = SSL_write(rssl->ssl, c->obuf, len);
|
||||
|
||||
if (rv > 0) {
|
||||
rssl->lastLen = 0;
|
||||
} else if (rv < 0) {
|
||||
rssl->lastLen = len;
|
||||
|
||||
int err = SSL_get_error(rssl->ssl, rv);
|
||||
if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
|
||||
return 0;
|
||||
} else {
|
||||
__redisSetError(c, REDIS_ERR_IO, NULL);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void redisSSLAsyncRead(redisAsyncContext *ac) {
|
||||
int rv;
|
||||
redisSSLContext *rssl = ac->c.privdata;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->wantRead = 0;
|
||||
|
||||
if (rssl->pendingWrite) {
|
||||
int done;
|
||||
|
||||
/* This is probably just a write event */
|
||||
rssl->pendingWrite = 0;
|
||||
rv = redisBufferWrite(c, &done);
|
||||
if (rv == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
} else if (!done) {
|
||||
_EL_ADD_WRITE(ac);
|
||||
}
|
||||
}
|
||||
|
||||
rv = redisBufferRead(c);
|
||||
if (rv == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
} else {
|
||||
_EL_ADD_READ(ac);
|
||||
redisProcessCallbacks(ac);
|
||||
}
|
||||
}
|
||||
|
||||
static void redisSSLAsyncWrite(redisAsyncContext *ac) {
|
||||
int rv, done = 0;
|
||||
redisSSLContext *rssl = ac->c.privdata;
|
||||
redisContext *c = &ac->c;
|
||||
|
||||
rssl->pendingWrite = 0;
|
||||
rv = redisBufferWrite(c, &done);
|
||||
if (rv == REDIS_ERR) {
|
||||
__redisAsyncDisconnect(ac);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!done) {
|
||||
if (rssl->wantRead) {
|
||||
/* Need to read-before-write */
|
||||
rssl->pendingWrite = 1;
|
||||
_EL_DEL_WRITE(ac);
|
||||
} else {
|
||||
/* No extra reads needed, just need to write more */
|
||||
_EL_ADD_WRITE(ac);
|
||||
}
|
||||
} else {
|
||||
/* Already done! */
|
||||
_EL_DEL_WRITE(ac);
|
||||
}
|
||||
|
||||
/* Always reschedule a read */
|
||||
_EL_ADD_READ(ac);
|
||||
}
|
||||
|
||||
redisContextFuncs redisContextSSLFuncs = {
|
||||
.free_privdata = redisSSLFreeContext,
|
||||
.async_read = redisSSLAsyncRead,
|
||||
.async_write = redisSSLAsyncWrite,
|
||||
.read = redisSSLRead,
|
||||
.write = redisSSLWrite
|
||||
};
|
||||
|
||||
Vendored
+10
-83
@@ -13,16 +13,12 @@
|
||||
#include <limits.h>
|
||||
|
||||
#include "hiredis.h"
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
#include "hiredis_ssl.h"
|
||||
#endif
|
||||
#include "net.h"
|
||||
|
||||
enum connection_type {
|
||||
CONN_TCP,
|
||||
CONN_UNIX,
|
||||
CONN_FD,
|
||||
CONN_SSL
|
||||
CONN_FD
|
||||
};
|
||||
|
||||
struct config {
|
||||
@@ -37,14 +33,6 @@ struct config {
|
||||
struct {
|
||||
const char *path;
|
||||
} unix_sock;
|
||||
|
||||
struct {
|
||||
const char *host;
|
||||
int port;
|
||||
const char *ca_cert;
|
||||
const char *cert;
|
||||
const char *key;
|
||||
} ssl;
|
||||
};
|
||||
|
||||
/* The following lines make up our testing "framework" :) */
|
||||
@@ -105,27 +93,11 @@ static int disconnect(redisContext *c, int keep_fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void do_ssl_handshake(redisContext *c, struct config config) {
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
redisSecureConnection(c, config.ssl.ca_cert, config.ssl.cert, config.ssl.key, NULL);
|
||||
if (c->err) {
|
||||
printf("SSL error: %s\n", c->errstr);
|
||||
redisFree(c);
|
||||
exit(1);
|
||||
}
|
||||
#else
|
||||
(void) c;
|
||||
(void) config;
|
||||
#endif
|
||||
}
|
||||
|
||||
static redisContext *do_connect(struct config config) {
|
||||
redisContext *c = NULL;
|
||||
|
||||
if (config.type == CONN_TCP) {
|
||||
c = redisConnect(config.tcp.host, config.tcp.port);
|
||||
} else if (config.type == CONN_SSL) {
|
||||
c = redisConnect(config.ssl.host, config.ssl.port);
|
||||
} else if (config.type == CONN_UNIX) {
|
||||
c = redisConnectUnix(config.unix_sock.path);
|
||||
} else if (config.type == CONN_FD) {
|
||||
@@ -149,21 +121,9 @@ static redisContext *do_connect(struct config config) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (config.type == CONN_SSL) {
|
||||
do_ssl_handshake(c, config);
|
||||
}
|
||||
|
||||
return select_database(c);
|
||||
}
|
||||
|
||||
static void do_reconnect(redisContext *c, struct config config) {
|
||||
redisReconnect(c);
|
||||
|
||||
if (config.type == CONN_SSL) {
|
||||
do_ssl_handshake(c, config);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_format_commands(void) {
|
||||
char *cmd;
|
||||
int len;
|
||||
@@ -400,8 +360,7 @@ static void test_reply_reader(void) {
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
#if LLONG_MAX > SIZE_MAX
|
||||
test("Set error when array > SIZE_MAX: ");
|
||||
test("Set error when array > INT_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "*9223372036854775807\r\n+asdf\r\n",29);
|
||||
ret = redisReaderGetReply(reader,&reply);
|
||||
@@ -410,6 +369,7 @@ static void test_reply_reader(void) {
|
||||
freeReplyObject(reply);
|
||||
redisReaderFree(reader);
|
||||
|
||||
#if LLONG_MAX > SIZE_MAX
|
||||
test("Set error when bulk > SIZE_MAX: ");
|
||||
reader = redisReaderCreate();
|
||||
redisReaderFeed(reader, "$9223372036854775807\r\nasdf\r\n",28);
|
||||
@@ -474,23 +434,22 @@ static void test_free_null(void) {
|
||||
test_cond(reply == NULL);
|
||||
}
|
||||
|
||||
#define HIREDIS_BAD_DOMAIN "idontexist-noreally.com"
|
||||
static void test_blocking_connection_errors(void) {
|
||||
redisContext *c;
|
||||
struct addrinfo hints = {.ai_family = AF_INET};
|
||||
struct addrinfo *ai_tmp = NULL;
|
||||
const char *bad_domain = "idontexist.com";
|
||||
|
||||
int rv = getaddrinfo(HIREDIS_BAD_DOMAIN, "6379", &hints, &ai_tmp);
|
||||
int rv = getaddrinfo(bad_domain, "6379", &hints, &ai_tmp);
|
||||
if (rv != 0) {
|
||||
// Address does *not* exist
|
||||
test("Returns error when host cannot be resolved: ");
|
||||
// First see if this domain name *actually* resolves to NXDOMAIN
|
||||
c = redisConnect(HIREDIS_BAD_DOMAIN, 6379);
|
||||
c = redisConnect("dontexist.com", 6379);
|
||||
test_cond(
|
||||
c->err == REDIS_ERR_OTHER &&
|
||||
(strcmp(c->errstr, "Name or service not known") == 0 ||
|
||||
strcmp(c->errstr, "Can't resolve: " HIREDIS_BAD_DOMAIN) == 0 ||
|
||||
strcmp(c->errstr, "Name does not resolve") == 0 ||
|
||||
strcmp(c->errstr, "Can't resolve: sadkfjaskfjsa.com") == 0 ||
|
||||
strcmp(c->errstr,
|
||||
"nodename nor servname provided, or not known") == 0 ||
|
||||
strcmp(c->errstr, "No address associated with hostname") == 0 ||
|
||||
@@ -615,8 +574,7 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
|
||||
c = do_connect(config);
|
||||
test("Does not return a reply when the command times out: ");
|
||||
redisAppendFormattedCommand(c, cmd, strlen(cmd));
|
||||
s = c->funcs->write(c);
|
||||
s = write(c->fd, cmd, strlen(cmd));
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 10000;
|
||||
redisSetTimeout(c, tv);
|
||||
@@ -625,7 +583,7 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
freeReplyObject(reply);
|
||||
|
||||
test("Reconnect properly reconnects after a timeout: ");
|
||||
do_reconnect(c, config);
|
||||
redisReconnect(c);
|
||||
reply = redisCommand(c, "PING");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
|
||||
freeReplyObject(reply);
|
||||
@@ -633,7 +591,7 @@ static void test_blocking_connection_timeouts(struct config config) {
|
||||
test("Reconnect properly uses owned parameters: ");
|
||||
config.tcp.host = "foo";
|
||||
config.unix_sock.path = "foo";
|
||||
do_reconnect(c, config);
|
||||
redisReconnect(c);
|
||||
reply = redisCommand(c, "PING");
|
||||
test_cond(reply != NULL && reply->type == REDIS_REPLY_STATUS && strcmp(reply->str, "PONG") == 0);
|
||||
freeReplyObject(reply);
|
||||
@@ -936,23 +894,6 @@ int main(int argc, char **argv) {
|
||||
throughput = 0;
|
||||
} else if (argc >= 1 && !strcmp(argv[0],"--skip-inherit-fd")) {
|
||||
test_inherit_fd = 0;
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-port")) {
|
||||
argv++; argc--;
|
||||
cfg.ssl.port = atoi(argv[0]);
|
||||
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-host")) {
|
||||
argv++; argc--;
|
||||
cfg.ssl.host = argv[0];
|
||||
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-ca-cert")) {
|
||||
argv++; argc--;
|
||||
cfg.ssl.ca_cert = argv[0];
|
||||
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-cert")) {
|
||||
argv++; argc--;
|
||||
cfg.ssl.cert = argv[0];
|
||||
} else if (argc >= 2 && !strcmp(argv[0],"--ssl-key")) {
|
||||
argv++; argc--;
|
||||
cfg.ssl.key = argv[0];
|
||||
#endif
|
||||
} else {
|
||||
fprintf(stderr, "Invalid argument: %s\n", argv[0]);
|
||||
exit(1);
|
||||
@@ -981,20 +922,6 @@ int main(int argc, char **argv) {
|
||||
test_blocking_io_errors(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
|
||||
#ifdef HIREDIS_TEST_SSL
|
||||
if (cfg.ssl.port && cfg.ssl.host) {
|
||||
printf("\nTesting against SSL connection (%s:%d):\n", cfg.ssl.host, cfg.ssl.port);
|
||||
cfg.type = CONN_SSL;
|
||||
|
||||
test_blocking_connection(cfg);
|
||||
test_blocking_connection_timeouts(cfg);
|
||||
test_blocking_io_errors(cfg);
|
||||
test_invalid_timeout_errors(cfg);
|
||||
test_append_formatted_commands(cfg);
|
||||
if (throughput) test_throughput(cfg);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (test_inherit_fd) {
|
||||
printf("\nTesting against inherited fd (%s):\n", cfg.unix_sock.path);
|
||||
cfg.type = CONN_FD;
|
||||
|
||||
Vendored
-70
@@ -1,70 +0,0 @@
|
||||
#!/bin/sh -ue
|
||||
|
||||
REDIS_SERVER=${REDIS_SERVER:-redis-server}
|
||||
REDIS_PORT=${REDIS_PORT:-56379}
|
||||
REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}
|
||||
TEST_SSL=${TEST_SSL:-0}
|
||||
SSL_TEST_ARGS=
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
PID_FILE=${tmpdir}/hiredis-test-redis.pid
|
||||
SOCK_FILE=${tmpdir}/hiredis-test-redis.sock
|
||||
|
||||
if [ "$TEST_SSL" = "1" ]; then
|
||||
SSL_CA_CERT=${tmpdir}/ca.crt
|
||||
SSL_CA_KEY=${tmpdir}/ca.key
|
||||
SSL_CERT=${tmpdir}/redis.crt
|
||||
SSL_KEY=${tmpdir}/redis.key
|
||||
|
||||
openssl genrsa -out ${tmpdir}/ca.key 4096
|
||||
openssl req \
|
||||
-x509 -new -nodes -sha256 \
|
||||
-key ${SSL_CA_KEY} \
|
||||
-days 3650 \
|
||||
-subj '/CN=Hiredis Test CA' \
|
||||
-out ${SSL_CA_CERT}
|
||||
openssl genrsa -out ${SSL_KEY} 2048
|
||||
openssl req \
|
||||
-new -sha256 \
|
||||
-key ${SSL_KEY} \
|
||||
-subj '/CN=Hiredis Test Cert' | \
|
||||
openssl x509 \
|
||||
-req -sha256 \
|
||||
-CA ${SSL_CA_CERT} \
|
||||
-CAkey ${SSL_CA_KEY} \
|
||||
-CAserial ${tmpdir}/ca.txt \
|
||||
-CAcreateserial \
|
||||
-days 365 \
|
||||
-out ${SSL_CERT}
|
||||
|
||||
SSL_TEST_ARGS="--ssl-host 127.0.0.1 --ssl-port ${REDIS_SSL_PORT} --ssl-ca-cert ${SSL_CA_CERT} --ssl-cert ${SSL_CERT} --ssl-key ${SSL_KEY}"
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
set +e
|
||||
kill $(cat ${PID_FILE})
|
||||
rm -rf ${tmpdir}
|
||||
}
|
||||
trap cleanup INT TERM EXIT
|
||||
|
||||
cat > ${tmpdir}/redis.conf <<EOF
|
||||
daemonize yes
|
||||
pidfile ${PID_FILE}
|
||||
port ${REDIS_PORT}
|
||||
bind 127.0.0.1
|
||||
unixsocket ${SOCK_FILE}
|
||||
EOF
|
||||
|
||||
if [ "$TEST_SSL" = "1" ]; then
|
||||
cat >> ${tmpdir}/redis.conf <<EOF
|
||||
tls-port ${REDIS_SSL_PORT}
|
||||
tls-ca-cert-file ${SSL_CA_CERT}
|
||||
tls-cert-file ${SSL_CERT}
|
||||
tls-key-file ${SSL_KEY}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat ${tmpdir}/redis.conf
|
||||
${REDIS_SERVER} ${tmpdir}/redis.conf
|
||||
|
||||
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS}
|
||||
Vendored
+2
-16
@@ -2,20 +2,10 @@
|
||||
#define _WIN32_HELPER_INCLUDE
|
||||
#ifdef _MSC_VER
|
||||
|
||||
#include <winsock2.h> /* for struct timeval */
|
||||
|
||||
#ifndef inline
|
||||
#define inline __inline
|
||||
#endif
|
||||
|
||||
#ifndef strcasecmp
|
||||
#define strcasecmp stricmp
|
||||
#endif
|
||||
|
||||
#ifndef strncasecmp
|
||||
#define strncasecmp strnicmp
|
||||
#endif
|
||||
|
||||
#ifndef va_copy
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
#endif
|
||||
@@ -47,10 +37,6 @@ __inline int c99_snprintf(char* str, size_t size, const char* format, ...)
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#ifdef _WIN32
|
||||
#define strerror_r(errno,buf,len) strerror_s(buf,len,errno)
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#endif /* _WIN32_HELPER_INCLUDE */
|
||||
#endif
|
||||
#endif
|
||||
@@ -216,7 +216,7 @@ ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
|
||||
}
|
||||
|
||||
JEMALLOC_ALWAYS_INLINE int
|
||||
iget_defrag_hint(tsdn_t *tsdn, void* ptr) {
|
||||
iget_defrag_hint(tsdn_t *tsdn, void* ptr, int *bin_util, int *run_util) {
|
||||
int defrag = 0;
|
||||
rtree_ctx_t rtree_ctx_fallback;
|
||||
rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
|
||||
@@ -232,22 +232,11 @@ iget_defrag_hint(tsdn_t *tsdn, void* ptr) {
|
||||
malloc_mutex_lock(tsdn, &bin->lock);
|
||||
/* don't bother moving allocations from the slab currently used for new allocations */
|
||||
if (slab != bin->slabcur) {
|
||||
int free_in_slab = extent_nfree_get(slab);
|
||||
if (free_in_slab) {
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
int curslabs = bin->stats.curslabs;
|
||||
size_t curregs = bin->stats.curregs;
|
||||
if (bin->slabcur) {
|
||||
/* remove slabcur from the overall utilization */
|
||||
curregs -= bin_info->nregs - extent_nfree_get(bin->slabcur);
|
||||
curslabs -= 1;
|
||||
}
|
||||
/* Compare the utilization ratio of the slab in question to the total average,
|
||||
* to avoid precision lost and division, we do that by extrapolating the usage
|
||||
* of the slab as if all slabs have the same usage. If this slab is less used
|
||||
* than the average, we'll prefer to evict the data to hopefully more used ones */
|
||||
defrag = (bin_info->nregs - free_in_slab) * curslabs <= curregs;
|
||||
}
|
||||
const bin_info_t *bin_info = &bin_infos[binind];
|
||||
size_t availregs = bin_info->nregs * bin->stats.curslabs;
|
||||
*bin_util = ((long long)bin->stats.curregs<<16) / availregs;
|
||||
*run_util = ((long long)(bin_info->nregs - extent_nfree_get(slab))<<16) / bin_info->nregs;
|
||||
defrag = 1;
|
||||
}
|
||||
malloc_mutex_unlock(tsdn, &bin->lock);
|
||||
}
|
||||
|
||||
+1
-7
@@ -787,13 +787,7 @@ background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {
|
||||
nstime_init(&stats->run_interval, 0);
|
||||
for (unsigned i = 0; i < max_background_threads; i++) {
|
||||
background_thread_info_t *info = &background_thread_info[i];
|
||||
if (malloc_mutex_trylock(tsdn, &info->mtx)) {
|
||||
/*
|
||||
* Each background thread run may take a long time;
|
||||
* avoid waiting on the stats if the thread is active.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
malloc_mutex_lock(tsdn, &info->mtx);
|
||||
if (info->state != background_thread_stopped) {
|
||||
num_runs += info->tot_n_runs;
|
||||
nstime_add(&stats->run_interval, &info->tot_sleep_time);
|
||||
|
||||
Vendored
+5
-3
@@ -3326,10 +3326,12 @@ jemalloc_postfork_child(void) {
|
||||
/******************************************************************************/
|
||||
|
||||
/* Helps the application decide if a pointer is worth re-allocating in order to reduce fragmentation.
|
||||
* returns 1 if the allocation should be moved, and 0 if the allocation be kept.
|
||||
* returns 0 if the allocation is in the currently active run,
|
||||
* or when it is not causing any frag issue (large or huge bin)
|
||||
* returns the bin utilization and run utilization both in fixed point 16:16.
|
||||
* If the application decides to re-allocate it should use MALLOCX_TCACHE_NONE when doing so. */
|
||||
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
|
||||
get_defrag_hint(void* ptr) {
|
||||
get_defrag_hint(void* ptr, int *bin_util, int *run_util) {
|
||||
assert(ptr != NULL);
|
||||
return iget_defrag_hint(TSDN_NULL, ptr);
|
||||
return iget_defrag_hint(TSDN_NULL, ptr, bin_util, run_util);
|
||||
}
|
||||
Vendored
+1
-24
@@ -21,7 +21,7 @@ So what usually happens is either:
|
||||
|
||||
The result is a pollution of binaries without line editing support.
|
||||
|
||||
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporting line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.
|
||||
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporing line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to Linenoise if not.
|
||||
|
||||
## Terminals, in 2010.
|
||||
|
||||
@@ -126,24 +126,6 @@ Linenoise has direct support for persisting the history into an history
|
||||
file. The functions `linenoiseHistorySave` and `linenoiseHistoryLoad` do
|
||||
just that. Both functions return -1 on error and 0 on success.
|
||||
|
||||
## Mask mode
|
||||
|
||||
Sometimes it is useful to allow the user to type passwords or other
|
||||
secrets that should not be displayed. For such situations linenoise supports
|
||||
a "mask mode" that will just replace the characters the user is typing
|
||||
with `*` characters, like in the following example:
|
||||
|
||||
$ ./linenoise_example
|
||||
hello> get mykey
|
||||
echo: 'get mykey'
|
||||
hello> /mask
|
||||
hello> *********
|
||||
|
||||
You can enable and disable mask mode using the following two functions:
|
||||
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
## Completion
|
||||
|
||||
Linenoise supports completion, which is the ability to complete the user
|
||||
@@ -240,8 +222,3 @@ Sometimes you may want to clear the screen as a result of something the
|
||||
user typed. You can do this by calling the following function:
|
||||
|
||||
void linenoiseClearScreen(void);
|
||||
|
||||
## Related projects
|
||||
|
||||
* [Linenoise NG](https://github.com/arangodb/linenoise-ng) is a fork of Linenoise that aims to add more advanced features like UTF-8 support, Windows support and other features. Uses C++ instead of C as development language.
|
||||
* [Linenoise-swift](https://github.com/andybest/linenoise-swift) is a reimplementation of Linenoise written in Swift.
|
||||
Vendored
-5
@@ -55,7 +55,6 @@ int main(int argc, char **argv) {
|
||||
*
|
||||
* The typed string is returned as a malloc() allocated string by
|
||||
* linenoise, so the user needs to free() it. */
|
||||
|
||||
while((line = linenoise("hello> ")) != NULL) {
|
||||
/* Do something with the string. */
|
||||
if (line[0] != '\0' && line[0] != '/') {
|
||||
@@ -66,10 +65,6 @@ int main(int argc, char **argv) {
|
||||
/* The "/historylen" command will change the history len. */
|
||||
int len = atoi(line+11);
|
||||
linenoiseHistorySetMaxLen(len);
|
||||
} else if (!strncmp(line, "/mask", 5)) {
|
||||
linenoiseMaskModeEnable();
|
||||
} else if (!strncmp(line, "/unmask", 7)) {
|
||||
linenoiseMaskModeDisable();
|
||||
} else if (line[0] == '/') {
|
||||
printf("Unreconized command: %s\n", line);
|
||||
}
|
||||
|
||||
Vendored
+3
-29
@@ -125,7 +125,6 @@ static linenoiseHintsCallback *hintsCallback = NULL;
|
||||
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
|
||||
|
||||
static struct termios orig_termios; /* In order to restore at exit.*/
|
||||
static int maskmode = 0; /* Show "***" instead of input. For passwords. */
|
||||
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
|
||||
static int mlmode = 0; /* Multi line mode. Default is single line. */
|
||||
static int atexit_registered = 0; /* Register atexit just 1 time. */
|
||||
@@ -198,19 +197,6 @@ FILE *lndebug_fp = NULL;
|
||||
|
||||
/* ======================= Low level terminal handling ====================== */
|
||||
|
||||
/* Enable "mask mode". When it is enabled, instead of the input that
|
||||
* the user is typing, the terminal will just display a corresponding
|
||||
* number of asterisks, like "****". This is useful for passwords and other
|
||||
* secrets that should not be displayed. */
|
||||
void linenoiseMaskModeEnable(void) {
|
||||
maskmode = 1;
|
||||
}
|
||||
|
||||
/* Disable mask mode. */
|
||||
void linenoiseMaskModeDisable(void) {
|
||||
maskmode = 0;
|
||||
}
|
||||
|
||||
/* Set if to use or not the multi line mode. */
|
||||
void linenoiseSetMultiLine(int ml) {
|
||||
mlmode = ml;
|
||||
@@ -499,8 +485,6 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
||||
if (bold == 1 && color == -1) color = 37;
|
||||
if (color != -1 || bold != 0)
|
||||
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
|
||||
else
|
||||
seq[0] = '\0';
|
||||
abAppend(ab,seq,strlen(seq));
|
||||
abAppend(ab,hint,hintlen);
|
||||
if (color != -1 || bold != 0)
|
||||
@@ -539,11 +523,7 @@ static void refreshSingleLine(struct linenoiseState *l) {
|
||||
abAppend(&ab,seq,strlen(seq));
|
||||
/* Write the prompt and the current buffer content */
|
||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||
if (maskmode == 1) {
|
||||
while (len--) abAppend(&ab,"*",1);
|
||||
} else {
|
||||
abAppend(&ab,buf,len);
|
||||
}
|
||||
abAppend(&ab,buf,len);
|
||||
/* Show hits if any. */
|
||||
refreshShowHints(&ab,l,plen);
|
||||
/* Erase to right */
|
||||
@@ -597,12 +577,7 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
||||
|
||||
/* Write the prompt and the current buffer content */
|
||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||
if (maskmode == 1) {
|
||||
unsigned int i;
|
||||
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
|
||||
} else {
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
}
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
|
||||
/* Show hits if any. */
|
||||
refreshShowHints(&ab,l,plen);
|
||||
@@ -670,8 +645,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
||||
if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
|
||||
/* Avoid a full update of the line in the
|
||||
* trivial case. */
|
||||
char d = (maskmode==1) ? '*' : c;
|
||||
if (write(l->ofd,&d,1) == -1) return -1;
|
||||
if (write(l->ofd,&c,1) == -1) return -1;
|
||||
} else {
|
||||
refreshLine(l);
|
||||
}
|
||||
|
||||
Vendored
-2
@@ -65,8 +65,6 @@ int linenoiseHistoryLoad(const char *filename);
|
||||
void linenoiseClearScreen(void);
|
||||
void linenoiseSetMultiLine(int ml);
|
||||
void linenoisePrintKeyCodes(void);
|
||||
void linenoiseMaskModeEnable(void);
|
||||
void linenoiseMaskModeDisable(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Vendored
+4
-6
@@ -89,14 +89,12 @@ typedef struct Header {
|
||||
} Header;
|
||||
|
||||
|
||||
static int getnum (lua_State *L, const char **fmt, int df) {
|
||||
static int getnum (const char **fmt, int df) {
|
||||
if (!isdigit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
int a = 0;
|
||||
do {
|
||||
if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
|
||||
luaL_error(L, "integral size overflow");
|
||||
a = a*10 + *((*fmt)++) - '0';
|
||||
} while (isdigit(**fmt));
|
||||
return a;
|
||||
@@ -117,9 +115,9 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
|
||||
case 'f': return sizeof(float);
|
||||
case 'd': return sizeof(double);
|
||||
case 'x': return 1;
|
||||
case 'c': return getnum(L, fmt, 1);
|
||||
case 'c': return getnum(fmt, 1);
|
||||
case 'i': case 'I': {
|
||||
int sz = getnum(L, fmt, sizeof(int));
|
||||
int sz = getnum(fmt, sizeof(int));
|
||||
if (sz > MAXINTSIZE)
|
||||
luaL_error(L, "integral size %d is larger than limit of %d",
|
||||
sz, MAXINTSIZE);
|
||||
@@ -152,7 +150,7 @@ static void controloptions (lua_State *L, int opt, const char **fmt,
|
||||
case '>': h->endian = BIG; return;
|
||||
case '<': h->endian = LITTLE; return;
|
||||
case '!': {
|
||||
int a = getnum(L, fmt, MAXALIGN);
|
||||
int a = getnum(fmt, MAXALIGN);
|
||||
if (!isp2(a))
|
||||
luaL_error(L, "alignment %d is not a power of 2", a);
|
||||
h->align = a;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
="repl-stream-db"
|
||||
="repl-id"
|
||||
="repl-offset"
|
||||
="lua"
|
||||
="redis-ver"
|
||||
="ctime"
|
||||
="used-mem"
|
||||
="aof-preamble"
|
||||
="redis-bits"
|
||||
="mvcc-tstamp"
|
||||
="keydb-subexpire-key"
|
||||
="keydb-subexpire-when"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+180
-450
File diff suppressed because it is too large.
Load diff
@@ -1,28 +0,0 @@
|
||||
#!/bin/sh
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCLSH=""
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
|
||||
done
|
||||
|
||||
if [ -z $TCLSH ]
|
||||
then
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis test"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
make -C tests/modules && \
|
||||
$TCLSH tests/test_helper.tcl \
|
||||
--single unit/moduleapi/commandfilter \
|
||||
--single unit/moduleapi/fork \
|
||||
--single unit/moduleapi/testrdb \
|
||||
--single unit/moduleapi/infotest \
|
||||
--single unit/moduleapi/propagate \
|
||||
--single unit/moduleapi/hooks \
|
||||
--single unit/moduleapi/misc \
|
||||
--single unit/moduleapi/blockonkeys \
|
||||
--single unit/moduleapi/scan \
|
||||
--single unit/moduleapi/datatype \
|
||||
--single unit/moduleapi/auth \
|
||||
"${@}"
|
||||
@@ -102,18 +102,6 @@ sentinel monitor mymaster 127.0.0.1 6379 2
|
||||
#
|
||||
# sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
|
||||
|
||||
# sentinel auth-user <master-name> <username>
|
||||
#
|
||||
# This is useful in order to authenticate to instances having ACL capabilities,
|
||||
# that is, running Redis 6.0 or greater. When just auth-pass is provided the
|
||||
# Sentinel instance will authenticate to Redis using the old "AUTH <pass>"
|
||||
# method. When also an username is provided, it will use "AUTH <user> <pass>".
|
||||
# In the Redis servers side, the ACL to provide just minimal access to
|
||||
# Sentinel instances, should be configured along the following lines:
|
||||
#
|
||||
# user sentinel-user >somepassword +client +subscribe +publish \
|
||||
# +ping +info +multi +slaveof +config +client +exec on
|
||||
|
||||
# sentinel down-after-milliseconds <master-name> <milliseconds>
|
||||
#
|
||||
# Number of milliseconds the master (or any attached replica or sentinel) should
|
||||
@@ -124,14 +112,6 @@ sentinel monitor mymaster 127.0.0.1 6379 2
|
||||
# Default is 30 seconds.
|
||||
sentinel down-after-milliseconds mymaster 30000
|
||||
|
||||
# requirepass <password>
|
||||
#
|
||||
# You can configure Sentinel itself to require a password, however when doing
|
||||
# so Sentinel will try to authenticate with the same password to all the
|
||||
# other Sentinels. So you need to configure all your Sentinels in a given
|
||||
# group with the same "requirepass" password. Check the following documentation
|
||||
# for more info: https://redis.io/topics/sentinel
|
||||
|
||||
# sentinel parallel-syncs <master-name> <numreplicas>
|
||||
#
|
||||
# How many replicas we can reconfigure to point to the new replica simultaneously
|
||||
|
||||
+19
-96
@@ -20,8 +20,8 @@ DEPENDENCY_TARGETS=hiredis linenoise lua
|
||||
NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
STD=-std=c11 -pedantic -DREDIS_STATIC=''
|
||||
CXX_STD=-std=c++14 -pedantic -fno-rtti -D__STDC_FORMAT_MACROS
|
||||
STD=-std=c99 -pedantic -DREDIS_STATIC=''
|
||||
CXX_STD=-std=c++14 -pedantic -fno-rtti
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
ifneq (,$(findstring FreeBSD,$(uname_S)))
|
||||
STD+=-Wno-c11-extensions
|
||||
@@ -33,7 +33,6 @@ OPT=$(OPTIMIZATION)
|
||||
PREFIX?=/usr/local
|
||||
INSTALL_BIN=$(PREFIX)/bin
|
||||
INSTALL=install
|
||||
PKG_CONFIG?=pkg-config
|
||||
|
||||
# Default allocator defaults to Jemalloc if it's not an ARM
|
||||
MALLOC=libc
|
||||
@@ -46,15 +45,6 @@ endif
|
||||
endif
|
||||
|
||||
USEASM?=true
|
||||
|
||||
ifneq ($(SANITIZE),)
|
||||
CFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE
|
||||
CXXFLAGS+= -fsanitize=$(SANITIZE) -DSANITIZE
|
||||
LDFLAGS+= -fsanitize=$(SANITIZE)
|
||||
MALLOC=libc
|
||||
USEASM=false
|
||||
endif
|
||||
|
||||
# Do we use our assembly spinlock? X64 only
|
||||
ifeq ($(uname_S),Linux)
|
||||
ifeq ($(uname_M),x86_64)
|
||||
@@ -70,17 +60,14 @@ endif
|
||||
|
||||
ifeq ($(COMPILER_NAME),clang)
|
||||
CXXFLAGS+= -stdlib=libc++
|
||||
LDFLAGS+= -latomic
|
||||
endif
|
||||
|
||||
# To get ARM stack traces if Redis crashes we need a special C flag.
|
||||
ifneq (,$(filter aarch64 armv,$(uname_M)))
|
||||
CFLAGS+=-funwind-tables
|
||||
CXXFLAGS+=-funwind-tables
|
||||
else
|
||||
ifneq (,$(findstring armv,$(uname_M)))
|
||||
CFLAGS+=-funwind-tables
|
||||
CXXFLAGS+=-funwind-tables
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -105,36 +92,27 @@ endif
|
||||
-include .make-settings
|
||||
|
||||
FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_CXXFLAGS=$(CXX_STD) $(WARN) $(OPT) $(DEBUG) $(CXXFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_CXXFLAGS=$(CXX_STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(CXXFLAGS) $(REDIS_CFLAGS)
|
||||
FINAL_LDFLAGS=$(LDFLAGS) $(REDIS_LDFLAGS) $(DEBUG)
|
||||
FINAL_LIBS+=-lm -lcurl
|
||||
FINAL_LIBS=-lm
|
||||
DEBUG=-g -ggdb
|
||||
|
||||
ifneq ($(uname_S),Darwin)
|
||||
FINAL_LIBS+=-latomic
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(uname_S),SunOS)
|
||||
# SunOS
|
||||
ifneq ($(@@),32bit)
|
||||
CFLAGS+= -m64
|
||||
CXXFLAGS+= -m64
|
||||
LDFLAGS+= -m64
|
||||
endif
|
||||
DEBUG=-g
|
||||
DEBUG_FLAGS=-g
|
||||
export CFLAGS CXXFLAGS LDFLAGS DEBUG DEBUG_FLAGS
|
||||
export CFLAGS LDFLAGS DEBUG DEBUG_FLAGS
|
||||
INSTALL=cp -pf
|
||||
FINAL_CFLAGS+= -D__EXTENSIONS__ -D_XPG6
|
||||
FINAL_CXXFLAGS+= -D__EXTENSIONS__ -D_XPG6
|
||||
FINAL_LIBS+= -ldl -lnsl -lsocket -lresolv -lpthread -lrt
|
||||
else
|
||||
ifeq ($(uname_S),Darwin)
|
||||
# Darwin
|
||||
FINAL_LIBS+= -ldl
|
||||
OPENSSL_CFLAGS=-I/usr/local/opt/openssl/include
|
||||
OPENSSL_LDFLAGS=-L/usr/local/opt/openssl/lib
|
||||
else
|
||||
ifeq ($(uname_S),AIX)
|
||||
# AIX
|
||||
@@ -146,7 +124,6 @@ ifeq ($(uname_S),OpenBSD)
|
||||
FINAL_LIBS+= -lpthread
|
||||
ifeq ($(USE_BACKTRACE),yes)
|
||||
FINAL_CFLAGS+= -DUSE_BACKTRACE -I/usr/local/include
|
||||
FINAL_CXXFLAGS+= -DUSE_BACKTRACE -I/usr/local/include
|
||||
FINAL_LDFLAGS+= -L/usr/local/lib
|
||||
FINAL_LIBS+= -lexecinfo
|
||||
endif
|
||||
@@ -159,22 +136,10 @@ else
|
||||
ifeq ($(uname_S),DragonFly)
|
||||
# FreeBSD
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),OpenBSD)
|
||||
# OpenBSD
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
ifeq ($(uname_S),NetBSD)
|
||||
# NetBSD
|
||||
FINAL_LIBS+= -lpthread -lexecinfo
|
||||
else
|
||||
# All the other OSes (notably Linux)
|
||||
FINAL_LDFLAGS+= -rdynamic
|
||||
FINAL_LIBS+=-ldl -pthread -lrt -luuid
|
||||
FINAL_CFLAGS += -DMOTD
|
||||
FINAL_CXXFLAGS += -DMOTD
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
@@ -185,30 +150,6 @@ endif
|
||||
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
|
||||
FINAL_CXXFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
|
||||
|
||||
# Determine systemd support and/or build preference (defaulting to auto-detection)
|
||||
BUILD_WITH_SYSTEMD=no
|
||||
# If 'USE_SYSTEMD' in the environment is neither "no" nor "yes", try to
|
||||
# auto-detect libsystemd's presence and link accordingly.
|
||||
ifneq ($(USE_SYSTEMD),no)
|
||||
LIBSYSTEMD_PKGCONFIG := $(shell $(PKG_CONFIG) --exists libsystemd && echo $$?)
|
||||
# If libsystemd cannot be detected, continue building without support for it
|
||||
# (unless a later check tells us otherwise)
|
||||
ifeq ($(LIBSYSTEMD_PKGCONFIG),0)
|
||||
BUILD_WITH_SYSTEMD=yes
|
||||
endif
|
||||
endif
|
||||
ifeq ($(USE_SYSTEMD),yes)
|
||||
ifneq ($(LIBSYSTEMD_PKGCONFIG),0)
|
||||
$(error USE_SYSTEMD is set to "$(USE_SYSTEMD)", but $(PKG_CONFIG) cannot find libsystemd)
|
||||
endif
|
||||
# Force building with libsystemd
|
||||
BUILD_WITH_SYSTEMD=yes
|
||||
endif
|
||||
ifeq ($(BUILD_WITH_SYSTEMD),yes)
|
||||
FINAL_LIBS+=$(shell $(PKG_CONFIG) --libs libsystemd)
|
||||
FINAL_CFLAGS+= -DHAVE_LIBSYSTEMD
|
||||
endif
|
||||
|
||||
ifeq ($(MALLOC),tcmalloc)
|
||||
FINAL_CFLAGS+= -DUSE_TCMALLOC
|
||||
FINAL_CXXFLAGS+= -DUSE_TCMALLOC
|
||||
@@ -235,16 +176,9 @@ ifeq ($(MALLOC),memkind)
|
||||
FINAL_LIBS := ../deps/memkind/src/.libs/libmemkind.a -lnuma $(FINAL_LIBS)
|
||||
endif
|
||||
|
||||
ifeq ($(BUILD_TLS),yes)
|
||||
FINAL_CFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
FINAL_CXXFLAGS+=-DUSE_OPENSSL $(OPENSSL_CXXFLAGS)
|
||||
FINAL_LDFLAGS+=$(OPENSSL_LDFLAGS)
|
||||
FINAL_LIBS += ../deps/hiredis/libhiredis_ssl.a -lssl -lcrypto
|
||||
endif
|
||||
|
||||
REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS)
|
||||
REDIS_CXX=$(QUIET_CC)$(CXX) $(FINAL_CXXFLAGS)
|
||||
KEYDB_AS=$(QUIET_CC) as --64 -g
|
||||
REDIS_CXX=$(QUIET_CC)$(CC) $(FINAL_CXXFLAGS)
|
||||
REDIS_NASM=$(QUIET_CC)nasm -felf64
|
||||
REDIS_LD=$(QUIET_LINK)$(CXX) $(FINAL_LDFLAGS)
|
||||
REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL)
|
||||
|
||||
@@ -256,18 +190,18 @@ MAKECOLOR="\033[32;1m"
|
||||
ENDCOLOR="\033[0m"
|
||||
|
||||
ifndef V
|
||||
QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR);
|
||||
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
|
||||
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
|
||||
QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2;
|
||||
endif
|
||||
|
||||
REDIS_SERVER_NAME=keydb-server
|
||||
REDIS_SENTINEL_NAME=keydb-sentinel
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o acl.o storage.o rdb-s3.o fastlock.o new.o tracking.o cron.o connection.o tls.o sha256.o motd.o timeout.o setcpuaffinity.o $(ASM_OBJ)
|
||||
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o acl.o storage.o rdb-s3.o fastlock.o gopher.o $(ASM_OBJ)
|
||||
REDIS_CLI_NAME=keydb-cli
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o redis-cli-cpphelper.o zmalloc.o release.o anet.o ae.o crcspeed.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o new.o motd.o $(ASM_OBJ)
|
||||
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o anet.o ae.o crc64.o siphash.o crc16.o storage-lite.o fastlock.o $(ASM_OBJ)
|
||||
REDIS_BENCHMARK_NAME=keydb-benchmark
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o storage-lite.o fastlock.o new.o $(ASM_OBJ)
|
||||
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o redis-benchmark.o storage-lite.o fastlock.o $(ASM_OBJ)
|
||||
REDIS_CHECK_RDB_NAME=keydb-check-rdb
|
||||
REDIS_CHECK_AOF_NAME=keydb-check-aof
|
||||
|
||||
@@ -291,15 +225,11 @@ persist-settings: distclean
|
||||
echo OPT=$(OPT) >> .make-settings
|
||||
echo MALLOC=$(MALLOC) >> .make-settings
|
||||
echo CFLAGS=$(CFLAGS) >> .make-settings
|
||||
echo CXXFLAGS=$(CXXFLAGS) >> .make-settings
|
||||
echo LDFLAGS=$(LDFLAGS) >> .make-settings
|
||||
echo REDIS_CFLAGS=$(REDIS_CFLAGS) >> .make-settings
|
||||
echo REDIS_CXXFLAGS=$(REDIS_CXXFLAGS) >> .make-settings
|
||||
echo REDIS_LDFLAGS=$(REDIS_LDFLAGS) >> .make-settings
|
||||
echo PREV_FINAL_CFLAGS=$(FINAL_CFLAGS) >> .make-settings
|
||||
echo PREV_FINAL_CXXFLAGS=$(FINAL_CXXFLAGS) >> .make-settings
|
||||
echo PREV_FINAL_LDFLAGS=$(FINAL_LDFLAGS) >> .make-settings
|
||||
-(cd modules && $(MAKE))
|
||||
-(cd ../deps && $(MAKE) $(DEPENDENCY_TARGETS))
|
||||
|
||||
.PHONY: persist-settings
|
||||
@@ -308,8 +238,6 @@ persist-settings: distclean
|
||||
# Clean everything, persist settings and build dependencies if anything changed
|
||||
ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS)))
|
||||
.make-prerequisites: persist-settings
|
||||
else ifneq ($(strip $(PREV_FINAL_CXXFLAGS)), $(strip $(FINAL_CXXFLAGS)))
|
||||
.make-prerequisites: persist-settings
|
||||
else ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS)))
|
||||
.make-prerequisites: persist-settings
|
||||
else
|
||||
@@ -341,33 +269,28 @@ $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ)
|
||||
$(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ)
|
||||
$(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a $(FINAL_LIBS)
|
||||
|
||||
dict-benchmark: dict.cpp zmalloc.cpp sds.c siphash.c
|
||||
dict-benchmark: dict.c zmalloc.c sds.c siphash.c
|
||||
$(REDIS_CC) $(FINAL_CFLAGS) $^ -D DICT_BENCHMARK_MAIN -o $@ $(FINAL_LIBS)
|
||||
|
||||
DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d)
|
||||
-include $(DEP)
|
||||
|
||||
# Because the jemalloc.h header is generated as a part of the jemalloc build,
|
||||
# building it should complete before building any other object. Instead of
|
||||
# depending on a single artifact, build all dependencies first.
|
||||
%.o: %.c .make-prerequisites
|
||||
$(REDIS_CC) -MMD -o $@ -c $<
|
||||
$(REDIS_CC) -c $<
|
||||
|
||||
%.o: %.cpp .make-prerequisites
|
||||
$(REDIS_CXX) -MMD -o $@ -c $<
|
||||
$(REDIS_CXX) -c $<
|
||||
|
||||
%.o: %.asm .make-prerequisites
|
||||
$(KEYDB_AS) $< -o $@
|
||||
$(REDIS_NASM) $<
|
||||
|
||||
clean:
|
||||
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark
|
||||
rm -f $(DEP)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
distclean: clean
|
||||
-(cd ../deps && $(MAKE) distclean)
|
||||
-(cd modules && $(MAKE) clean)
|
||||
-(rm -f .make-*)
|
||||
|
||||
.PHONY: distclean
|
||||
@@ -387,7 +310,7 @@ lcov:
|
||||
@genhtml --legend -o lcov-html redis.info
|
||||
|
||||
test-sds: sds.c sds.h
|
||||
$(REDIS_CC) sds.c zmalloc.cpp -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
|
||||
$(REDIS_CC) sds.c zmalloc.c -DSDS_TEST_MAIN $(FINAL_LIBS) -o /tmp/sds_test
|
||||
/tmp/sds_test
|
||||
|
||||
.PHONY: lcov
|
||||
@@ -399,7 +322,7 @@ bench: $(REDIS_BENCHMARK_NAME)
|
||||
@echo ""
|
||||
@echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
|
||||
@echo ""
|
||||
$(MAKE) CXXFLAGS="-m32" CFLAGS="-m32" LDFLAGS="-m32"
|
||||
$(MAKE) CFLAGS="-m32" LDFLAGS="-m32"
|
||||
|
||||
gcov:
|
||||
$(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
|
||||
|
||||
+114
-449
File diff suppressed because it is too large.
Load diff
+3
-17
@@ -327,11 +327,12 @@ listNode *listIndex(list *list, long index) {
|
||||
}
|
||||
|
||||
/* Rotate the list removing the tail node and inserting it to the head. */
|
||||
void listRotateTailToHead(list *list) {
|
||||
void listRotate(list *list) {
|
||||
listNode *tail = list->tail;
|
||||
|
||||
if (listLength(list) <= 1) return;
|
||||
|
||||
/* Detach current tail */
|
||||
listNode *tail = list->tail;
|
||||
list->tail = tail->prev;
|
||||
list->tail->next = NULL;
|
||||
/* Move it as head */
|
||||
@@ -341,21 +342,6 @@ void listRotateTailToHead(list *list) {
|
||||
list->head = tail;
|
||||
}
|
||||
|
||||
/* Rotate the list removing the head node and inserting it to the tail. */
|
||||
void listRotateHeadToTail(list *list) {
|
||||
if (listLength(list) <= 1) return;
|
||||
|
||||
listNode *head = list->head;
|
||||
/* Detach current head */
|
||||
list->head = head->next;
|
||||
list->head->prev = NULL;
|
||||
/* Move it as tail */
|
||||
list->tail->next = head;
|
||||
head->next = NULL;
|
||||
head->prev = list->tail;
|
||||
list->tail = head;
|
||||
}
|
||||
|
||||
/* Add all the elements of the list 'o' at the end of the
|
||||
* list 'l'. The list 'other' remains empty but otherwise valid. */
|
||||
void listJoin(list *l, list *o) {
|
||||
|
||||
+3
-4
@@ -52,7 +52,7 @@ typedef struct list {
|
||||
listNode *head;
|
||||
listNode *tail;
|
||||
void *(*dup)(void *ptr);
|
||||
void (*free)(const void *ptr);
|
||||
void (*free)(void *ptr);
|
||||
int (*match)(void *ptr, void *key);
|
||||
unsigned long len;
|
||||
} list;
|
||||
@@ -70,7 +70,7 @@ typedef struct list {
|
||||
#define listSetMatchMethod(l,m) ((l)->match = (m))
|
||||
|
||||
#define listGetDupMethod(l) ((l)->dup)
|
||||
#define listGetFreeMethod(l) ((l)->free)
|
||||
#define listGetFree(l) ((l)->free)
|
||||
#define listGetMatchMethod(l) ((l)->match)
|
||||
|
||||
/* Prototypes */
|
||||
@@ -89,8 +89,7 @@ listNode *listSearchKey(list *list, void *key);
|
||||
listNode *listIndex(list *list, long index);
|
||||
void listRewind(list *list, listIter *li);
|
||||
void listRewindTail(list *list, listIter *li);
|
||||
void listRotateTailToHead(list *list);
|
||||
void listRotateHeadToTail(list *list);
|
||||
void listRotate(list *list);
|
||||
void listJoin(list *l, list *o);
|
||||
|
||||
/* Directions for iterators */
|
||||
|
||||
+31
-157
@@ -30,7 +30,6 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fmacros.h"
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
@@ -47,9 +46,10 @@
|
||||
|
||||
#include "ae.h"
|
||||
#include "fastlock.h"
|
||||
extern "C" {
|
||||
#include "zmalloc.h"
|
||||
#include "config.h"
|
||||
#include "mcs_lock.h"
|
||||
}
|
||||
|
||||
#ifdef USE_MUTEX
|
||||
thread_local int cOwnLock = 0;
|
||||
@@ -82,49 +82,11 @@ public:
|
||||
mutex_wrapper g_lock;
|
||||
|
||||
#else
|
||||
//fastlock g_lock("AE (global)");
|
||||
McsLock g_lock;
|
||||
|
||||
thread_local McsLock::node m_node;
|
||||
class McsLockGuard
|
||||
{
|
||||
McsLock &m_lock;
|
||||
bool fReleased = false;
|
||||
public:
|
||||
|
||||
McsLockGuard(McsLock &lock)
|
||||
: m_lock(lock)
|
||||
{
|
||||
m_lock.lock(&m_node);
|
||||
}
|
||||
|
||||
McsLockGuard(McsLock &lock, std::defer_lock_t)
|
||||
: m_lock(lock)
|
||||
{
|
||||
fReleased = true;
|
||||
}
|
||||
|
||||
void lock() {
|
||||
m_lock.lock(&m_node);
|
||||
fReleased = false;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
m_lock.unlock(&m_node);
|
||||
fReleased = true;
|
||||
}
|
||||
|
||||
~McsLockGuard()
|
||||
{
|
||||
if (!fReleased)
|
||||
m_lock.unlock(&m_node);
|
||||
}
|
||||
};
|
||||
|
||||
fastlock g_lock;
|
||||
#endif
|
||||
thread_local aeEventLoop *g_eventLoopThisThread = NULL;
|
||||
|
||||
#define AE_ASSERT(x) if (!(x)) do { fprintf(stderr, "AE_ASSERT FAILURE %s: %d\n", __FILE__, __LINE__); *((volatile int*)1) = 1; } while(0)
|
||||
#define AE_ASSERT(x) if (!(x)) do { fprintf(stderr, "AE_ASSER FAILURE\n"); *((volatile int*)0) = 1; } while(0)
|
||||
|
||||
/* Include the best multiplexing layer supported by this system.
|
||||
* The following should be ordered by performances, descending. */
|
||||
@@ -162,7 +124,6 @@ struct aeCommand
|
||||
AE_ASYNC_OP op;
|
||||
int fd;
|
||||
int mask;
|
||||
bool fLock = true;
|
||||
union {
|
||||
aePostFunctionProc *proc;
|
||||
aeFileProc *fproc;
|
||||
@@ -207,9 +168,7 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
|
||||
case AE_ASYNC_OP::PostFunction:
|
||||
{
|
||||
McsLockGuard ulock(g_lock, std::defer_lock);
|
||||
if (cmd.fLock)
|
||||
ulock.lock();
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock);
|
||||
((aePostFunctionProc*)cmd.proc)(cmd.clientData);
|
||||
break;
|
||||
}
|
||||
@@ -219,9 +178,7 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
if (cmd.pctl != nullptr)
|
||||
cmd.pctl->mutexcv.lock();
|
||||
|
||||
McsLockGuard ulock(g_lock, std::defer_lock);
|
||||
if (cmd.fLock)
|
||||
ulock.lock();
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock);
|
||||
(*cmd.pfn)();
|
||||
|
||||
if (cmd.pctl != nullptr)
|
||||
@@ -236,36 +193,6 @@ void aeProcessCmd(aeEventLoop *eventLoop, int fd, void *, int )
|
||||
}
|
||||
}
|
||||
|
||||
// Unlike write() this is an all or nothing thing. We will block if a partial write is hit
|
||||
ssize_t safe_write(int fd, const void *pv, size_t cb)
|
||||
{
|
||||
const char *pcb = (const char*)pv;
|
||||
ssize_t written = 0;
|
||||
do
|
||||
{
|
||||
ssize_t rval = write(fd, pcb, cb);
|
||||
if (rval > 0)
|
||||
{
|
||||
pcb += rval;
|
||||
cb -= rval;
|
||||
written += rval;
|
||||
}
|
||||
else if (errno == EAGAIN)
|
||||
{
|
||||
if (written == 0)
|
||||
break;
|
||||
// if we've already written something then we're committed so keep trying
|
||||
}
|
||||
else
|
||||
{
|
||||
if (rval == 0)
|
||||
return written;
|
||||
return rval;
|
||||
}
|
||||
} while (cb);
|
||||
return written;
|
||||
}
|
||||
|
||||
int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
aeFileProc *proc, void *clientData, int fSynchronous)
|
||||
{
|
||||
@@ -274,31 +201,28 @@ int aeCreateRemoteFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
|
||||
int ret = AE_OK;
|
||||
|
||||
aeCommand cmd = {};
|
||||
aeCommand cmd;
|
||||
cmd.op = AE_ASYNC_OP::CreateFileEvent;
|
||||
cmd.fd = fd;
|
||||
cmd.mask = mask;
|
||||
cmd.fproc = proc;
|
||||
cmd.clientData = clientData;
|
||||
cmd.pctl = nullptr;
|
||||
cmd.fLock = true;
|
||||
if (fSynchronous)
|
||||
{
|
||||
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl();
|
||||
cmd.pctl->mutexcv.lock();
|
||||
}
|
||||
cmd.pctl = new aeCommandControl();
|
||||
|
||||
auto size = safe_write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
|
||||
if (fSynchronous)
|
||||
cmd.pctl->mutexcv.lock();
|
||||
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
if (size != sizeof(cmd))
|
||||
{
|
||||
AE_ASSERT(size == sizeof(cmd) || size <= 0);
|
||||
AE_ASSERT(errno == EAGAIN);
|
||||
ret = AE_ERR;
|
||||
}
|
||||
|
||||
if (fSynchronous)
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
|
||||
cmd.pctl->cv.wait(ulock);
|
||||
ret = cmd.pctl->rval;
|
||||
delete cmd.pctl;
|
||||
@@ -314,18 +238,16 @@ int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg)
|
||||
proc(arg);
|
||||
return AE_OK;
|
||||
}
|
||||
aeCommand cmd = {};
|
||||
aeCommand cmd;
|
||||
cmd.op = AE_ASYNC_OP::PostFunction;
|
||||
cmd.proc = proc;
|
||||
cmd.clientData = arg;
|
||||
cmd.fLock = true;
|
||||
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
if (size != sizeof(cmd))
|
||||
return AE_ERR;
|
||||
AE_ASSERT(size == sizeof(cmd));
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynchronous, bool fLock)
|
||||
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynchronous)
|
||||
{
|
||||
if (eventLoop == g_eventLoopThisThread)
|
||||
{
|
||||
@@ -333,25 +255,20 @@ int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynch
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
aeCommand cmd = {};
|
||||
aeCommand cmd;
|
||||
cmd.op = AE_ASYNC_OP::PostCppFunction;
|
||||
cmd.pfn = new (MALLOC_LOCAL) std::function<void()>(fn);
|
||||
cmd.pfn = new std::function<void()>(fn);
|
||||
cmd.pctl = nullptr;
|
||||
cmd.fLock = fLock;
|
||||
if (fSynchronous)
|
||||
{
|
||||
cmd.pctl = new (MALLOC_LOCAL) aeCommandControl();
|
||||
cmd.pctl = new aeCommandControl();
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
|
||||
if (fSynchronous)
|
||||
cmd.pctl->mutexcv.lock();
|
||||
}
|
||||
|
||||
auto size = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
if (size != sizeof(cmd))
|
||||
return AE_ERR;
|
||||
AE_ASSERT(size == sizeof(cmd));
|
||||
int ret = AE_OK;
|
||||
if (fSynchronous)
|
||||
{
|
||||
std::unique_lock<std::mutex> ulock(cmd.pctl->mutexcv, std::defer_lock);
|
||||
cmd.pctl->cv.wait(ulock);
|
||||
ret = cmd.pctl->rval;
|
||||
delete cmd.pctl;
|
||||
@@ -375,14 +292,13 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
eventLoop->maxfd = -1;
|
||||
eventLoop->beforesleep = NULL;
|
||||
eventLoop->aftersleep = NULL;
|
||||
eventLoop->flags = 0;
|
||||
if (aeApiCreate(eventLoop) == -1) goto err;
|
||||
/* Events with mask == AE_NONE are not set. So let's initialize the
|
||||
* vector with it. */
|
||||
for (i = 0; i < setsize; i++)
|
||||
eventLoop->events[i].mask = AE_NONE;
|
||||
|
||||
fastlock_init(&eventLoop->flock, "event loop");
|
||||
fastlock_init(&eventLoop->flock);
|
||||
int rgfd[2];
|
||||
if (pipe(rgfd) < 0)
|
||||
goto err;
|
||||
@@ -409,14 +325,6 @@ int aeGetSetSize(aeEventLoop *eventLoop) {
|
||||
return eventLoop->setsize;
|
||||
}
|
||||
|
||||
/* Tells the next iteration/s of the event processing to set timeout of 0. */
|
||||
void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
|
||||
if (noWait)
|
||||
eventLoop->flags |= AE_DONT_WAIT;
|
||||
else
|
||||
eventLoop->flags &= ~AE_DONT_WAIT;
|
||||
}
|
||||
|
||||
/* Resize the maximum set size of the event loop.
|
||||
* If the requested set size is smaller than the current set size, but
|
||||
* there is already a file descriptor in use that is >= the requested
|
||||
@@ -447,19 +355,10 @@ extern "C" void aeDeleteEventLoop(aeEventLoop *eventLoop) {
|
||||
aeApiFree(eventLoop);
|
||||
zfree(eventLoop->events);
|
||||
zfree(eventLoop->fired);
|
||||
zfree(eventLoop);
|
||||
fastlock_free(&eventLoop->flock);
|
||||
close(eventLoop->fdCmdRead);
|
||||
close(eventLoop->fdCmdWrite);
|
||||
|
||||
/* Free the time events list. */
|
||||
auto *te = eventLoop->timeEventHead;
|
||||
while (te)
|
||||
{
|
||||
auto *teNext = te->next;
|
||||
zfree(te);
|
||||
te = teNext;
|
||||
}
|
||||
zfree(eventLoop);
|
||||
}
|
||||
|
||||
extern "C" void aeStop(aeEventLoop *eventLoop) {
|
||||
@@ -497,7 +396,6 @@ void aeDeleteFileEventAsync(aeEventLoop *eventLoop, int fd, int mask)
|
||||
cmd.op = AE_ASYNC_OP::DeleteFileEvent;
|
||||
cmd.fd = fd;
|
||||
cmd.mask = mask;
|
||||
cmd.fLock = true;
|
||||
auto cb = write(eventLoop->fdCmdWrite, &cmd, sizeof(cmd));
|
||||
AE_ASSERT(cb == sizeof(cmd));
|
||||
}
|
||||
@@ -575,7 +473,6 @@ extern "C" long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long millise
|
||||
te->clientData = clientData;
|
||||
te->prev = NULL;
|
||||
te->next = eventLoop->timeEventHead;
|
||||
te->refcount = 0;
|
||||
if (te->next)
|
||||
te->next->prev = te;
|
||||
eventLoop->timeEventHead = te;
|
||||
@@ -625,7 +522,7 @@ static aeTimeEvent *aeSearchNearestTimer(aeEventLoop *eventLoop)
|
||||
|
||||
/* Process time events */
|
||||
static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
McsLockGuard ulock(g_lock);
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock);
|
||||
int processed = 0;
|
||||
aeTimeEvent *te;
|
||||
long long maxId;
|
||||
@@ -657,13 +554,6 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
/* Remove events scheduled for deletion. */
|
||||
if (te->id == AE_DELETED_EVENT_ID) {
|
||||
aeTimeEvent *next = te->next;
|
||||
/* If a reference exists for this timer event,
|
||||
* don't free it. This is currently incremented
|
||||
* for recursive timerProc calls */
|
||||
if (te->refcount) {
|
||||
te = next;
|
||||
continue;
|
||||
}
|
||||
if (te->prev)
|
||||
te->prev->next = te->next;
|
||||
else
|
||||
@@ -693,9 +583,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
int retval;
|
||||
|
||||
id = te->id;
|
||||
te->refcount++;
|
||||
retval = te->timeProc(eventLoop, id, te->clientData);
|
||||
te->refcount--;
|
||||
processed++;
|
||||
if (retval != AE_NOMORE) {
|
||||
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
|
||||
@@ -711,7 +599,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
extern "C" void ProcessEventCore(aeEventLoop *eventLoop, aeFileEvent *fe, int mask, int fd)
|
||||
{
|
||||
#define LOCK_IF_NECESSARY(fe, tsmask) \
|
||||
McsLockGuard ulock(g_lock, std::defer_lock); \
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock); \
|
||||
if (!(fe->mask & tsmask)) \
|
||||
ulock.lock()
|
||||
|
||||
@@ -740,7 +628,6 @@ extern "C" void ProcessEventCore(aeEventLoop *eventLoop, aeFileEvent *fe, int ma
|
||||
LOCK_IF_NECESSARY(fe, AE_READ_THREADSAFE);
|
||||
fe->rfileProc(eventLoop,fd,fe->clientData,mask | (fe->mask & AE_READ_THREADSAFE));
|
||||
fired++;
|
||||
fe = &eventLoop->events[fd]; /* Refresh in case of resize. */
|
||||
}
|
||||
|
||||
/* Fire the writable event. */
|
||||
@@ -777,7 +664,6 @@ extern "C" void ProcessEventCore(aeEventLoop *eventLoop, aeFileEvent *fe, int ma
|
||||
* if flags has AE_DONT_WAIT set the function returns ASAP until all
|
||||
* the events that's possible to process without to wait are processed.
|
||||
* if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called.
|
||||
* if flags has AE_CALL_BEFORE_SLEEP set, the beforesleep callback is called.
|
||||
*
|
||||
* The function returns the number of events processed. */
|
||||
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
@@ -832,25 +718,13 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
}
|
||||
}
|
||||
|
||||
if (eventLoop->flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
tvp = &tv;
|
||||
}
|
||||
|
||||
if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP) {
|
||||
McsLockGuard ulock(g_lock, std::defer_lock);
|
||||
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE))
|
||||
ulock.lock();
|
||||
eventLoop->beforesleep(eventLoop);
|
||||
}
|
||||
|
||||
/* Call the multiplexing API, will return only on timeout or when
|
||||
* some event fires. */
|
||||
numevents = aeApiPoll(eventLoop, tvp);
|
||||
|
||||
/* After sleep callback. */
|
||||
if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP) {
|
||||
McsLockGuard ulock(g_lock, std::defer_lock);
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
if (!(eventLoop->aftersleepFlags & AE_SLEEP_THREADSAFE))
|
||||
ulock.lock();
|
||||
eventLoop->aftersleep(eventLoop);
|
||||
@@ -901,7 +775,7 @@ void aeMain(aeEventLoop *eventLoop) {
|
||||
g_eventLoopThisThread = eventLoop;
|
||||
while (!eventLoop->stop) {
|
||||
if (eventLoop->beforesleep != NULL) {
|
||||
McsLockGuard ulock(g_lock, std::defer_lock);
|
||||
std::unique_lock<decltype(g_lock)> ulock(g_lock, std::defer_lock);
|
||||
if (!(eventLoop->beforesleepFlags & AE_SLEEP_THREADSAFE))
|
||||
ulock.lock();
|
||||
eventLoop->beforesleep(eventLoop);
|
||||
@@ -928,20 +802,20 @@ void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep,
|
||||
|
||||
void aeAcquireLock()
|
||||
{
|
||||
g_lock.lock(&m_node);
|
||||
g_lock.lock();
|
||||
}
|
||||
|
||||
int aeTryAcquireLock(int fWeak)
|
||||
int aeTryAcquireLock()
|
||||
{
|
||||
return g_lock.try_lock(&m_node, fWeak);
|
||||
return g_lock.try_lock();
|
||||
}
|
||||
|
||||
void aeReleaseLock()
|
||||
{
|
||||
g_lock.unlock(&m_node);
|
||||
g_lock.unlock();
|
||||
}
|
||||
|
||||
int aeThreadOwnsLock()
|
||||
{
|
||||
return m_node.depth > 0;
|
||||
return g_lock.fOwnLock();
|
||||
}
|
||||
@@ -58,12 +58,11 @@ extern "C" {
|
||||
#define AE_WRITE_THREADSAFE 16
|
||||
#define AE_SLEEP_THREADSAFE 32
|
||||
|
||||
#define AE_FILE_EVENTS (1<<0)
|
||||
#define AE_TIME_EVENTS (1<<1)
|
||||
#define AE_FILE_EVENTS 1
|
||||
#define AE_TIME_EVENTS 2
|
||||
#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)
|
||||
#define AE_DONT_WAIT (1<<2)
|
||||
#define AE_CALL_BEFORE_SLEEP (1<<3)
|
||||
#define AE_CALL_AFTER_SLEEP (1<<4)
|
||||
#define AE_DONT_WAIT 4
|
||||
#define AE_CALL_AFTER_SLEEP 8
|
||||
|
||||
#define AE_NOMORE -1
|
||||
#define AE_DELETED_EVENT_ID -1
|
||||
@@ -98,8 +97,6 @@ typedef struct aeTimeEvent {
|
||||
void *clientData;
|
||||
struct aeTimeEvent *prev;
|
||||
struct aeTimeEvent *next;
|
||||
int refcount; /* refcount to prevent timer events from being
|
||||
* freed in recursive time event calls. */
|
||||
} aeTimeEvent;
|
||||
|
||||
/* A fired event */
|
||||
@@ -127,7 +124,6 @@ typedef struct aeEventLoop {
|
||||
int fdCmdWrite;
|
||||
int fdCmdRead;
|
||||
int cevents;
|
||||
int flags;
|
||||
} aeEventLoop;
|
||||
|
||||
/* Prototypes */
|
||||
@@ -135,7 +131,7 @@ aeEventLoop *aeCreateEventLoop(int setsize);
|
||||
int aePostFunction(aeEventLoop *eventLoop, aePostFunctionProc *proc, void *arg);
|
||||
#ifdef __cplusplus
|
||||
} // EXTERN C
|
||||
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynchronous = false, bool fLock = true);
|
||||
int aePostFunction(aeEventLoop *eventLoop, std::function<void()> fn, bool fSynchronous = false);
|
||||
extern "C" {
|
||||
#endif
|
||||
void aeDeleteEventLoop(aeEventLoop *eventLoop);
|
||||
@@ -161,10 +157,9 @@ void aeSetBeforeSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *beforesleep
|
||||
void aeSetAfterSleepProc(aeEventLoop *eventLoop, aeBeforeSleepProc *aftersleep, int flags);
|
||||
int aeGetSetSize(aeEventLoop *eventLoop);
|
||||
int aeResizeSetSize(aeEventLoop *eventLoop, int setsize);
|
||||
void aeSetDontWait(aeEventLoop *eventLoop, int noWait);
|
||||
|
||||
void aeAcquireLock();
|
||||
int aeTryAcquireLock(int fWeak);
|
||||
int aeTryAcquireLock();
|
||||
void aeReleaseLock();
|
||||
int aeThreadOwnsLock();
|
||||
|
||||
|
||||
+2
-2
@@ -125,8 +125,8 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
||||
|
||||
if (e->events & EPOLLIN) mask |= AE_READABLE;
|
||||
if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
|
||||
if (e->events & EPOLLERR) mask |= AE_WRITABLE|AE_READABLE;
|
||||
if (e->events & EPOLLHUP) mask |= AE_WRITABLE|AE_READABLE;
|
||||
if (e->events & EPOLLERR) mask |= AE_WRITABLE;
|
||||
if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
|
||||
eventLoop->fired[j].fd = e->data.fd;
|
||||
eventLoop->fired[j].mask = mask;
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class AeLocker
|
||||
{
|
||||
bool m_fArmed = false;
|
||||
|
||||
public:
|
||||
AeLocker()
|
||||
{
|
||||
}
|
||||
|
||||
void arm(client *c) // if a client is passed, then the client is already locked
|
||||
{
|
||||
if (m_fArmed)
|
||||
return;
|
||||
|
||||
serverAssertDebug(!GlobalLocksAcquired());
|
||||
|
||||
if (c != nullptr)
|
||||
{
|
||||
serverAssert(!m_fArmed);
|
||||
serverAssert(c->lock.fOwnLock());
|
||||
|
||||
if (!aeTryAcquireLock(true /*fWeak*/)) // avoid locking the client if we can
|
||||
{
|
||||
bool fOwnClientLock = true;
|
||||
int clientNesting = 1;
|
||||
for (;;)
|
||||
{
|
||||
if (fOwnClientLock)
|
||||
{
|
||||
clientNesting = c->lock.unlock_recursive();
|
||||
fOwnClientLock = false;
|
||||
}
|
||||
aeAcquireLock();
|
||||
if (!c->lock.try_lock(false)) // ensure a strong try because aeAcquireLock is expensive
|
||||
{
|
||||
aeReleaseLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
c->lock.lock_recursive(clientNesting);
|
||||
}
|
||||
|
||||
m_fArmed = true;
|
||||
}
|
||||
else if (!m_fArmed)
|
||||
{
|
||||
m_fArmed = true;
|
||||
aeAcquireLock();
|
||||
}
|
||||
}
|
||||
|
||||
void disarm()
|
||||
{
|
||||
serverAssert(m_fArmed);
|
||||
m_fArmed = false;
|
||||
aeReleaseLock();
|
||||
}
|
||||
|
||||
bool isArmed() const
|
||||
{
|
||||
return m_fArmed;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
m_fArmed = false;
|
||||
}
|
||||
|
||||
~AeLocker()
|
||||
{
|
||||
if (m_fArmed)
|
||||
aeReleaseLock();
|
||||
}
|
||||
};
|
||||
+18
-36
@@ -193,20 +193,6 @@ int anetSendTimeout(char *err, int fd, long long ms) {
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
/* Set the socket receive timeout (SO_RCVTIMEO socket option) to the specified
|
||||
* number of milliseconds, or disable it if the 'ms' argument is zero. */
|
||||
int anetRecvTimeout(char *err, int fd, long long ms) {
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = ms/1000;
|
||||
tv.tv_usec = (ms%1000)*1000;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_RCVTIMEO: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
/* anetGenericResolve() is called by anetResolve() and anetResolveIP() to
|
||||
* do the actual work. It resolves the hostname "host" and set the string
|
||||
* representation of the IP address into the buffer pointed by "ipbuf".
|
||||
@@ -290,8 +276,8 @@ static int anetCreateSocket(char *err, int domain) {
|
||||
#define ANET_CONNECT_NONBLOCK 1
|
||||
#define ANET_CONNECT_BE_BINDING 2 /* Best effort binding. */
|
||||
#define ANET_CONNECT_REUSEPORT 4
|
||||
static int anetTcpGenericConnect(char *err, const char *addr, int port,
|
||||
const char *source_addr, int flags)
|
||||
static int anetTcpGenericConnect(char *err, char *addr, int port,
|
||||
char *source_addr, int flags)
|
||||
{
|
||||
int s = ANET_ERR, rv;
|
||||
char portstr[6]; /* strlen("65535") + 1; */
|
||||
@@ -373,31 +359,31 @@ end:
|
||||
}
|
||||
}
|
||||
|
||||
int anetTcpConnect(char *err, const char *addr, int port)
|
||||
int anetTcpConnect(char *err, char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONE);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockConnect(char *err, const char *addr, int port)
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,NULL,ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port,
|
||||
const char *source_addr)
|
||||
int anetTcpNonBlockBindConnect(char *err, char *addr, int port,
|
||||
char *source_addr)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,source_addr,
|
||||
ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
|
||||
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port,
|
||||
const char *source_addr)
|
||||
int anetTcpNonBlockBestEffortBindConnect(char *err, char *addr, int port,
|
||||
char *source_addr)
|
||||
{
|
||||
return anetTcpGenericConnect(err,addr,port,source_addr,
|
||||
ANET_CONNECT_NONBLOCK|ANET_CONNECT_BE_BINDING);
|
||||
}
|
||||
|
||||
int anetUnixGenericConnect(char *err, const char *path, int flags)
|
||||
int anetUnixGenericConnect(char *err, char *path, int flags)
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_un sa;
|
||||
@@ -425,12 +411,12 @@ int anetUnixGenericConnect(char *err, const char *path, int flags)
|
||||
return s;
|
||||
}
|
||||
|
||||
int anetUnixConnect(char *err, const char *path)
|
||||
int anetUnixConnect(char *err, char *path)
|
||||
{
|
||||
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONE);
|
||||
}
|
||||
|
||||
int anetUnixNonBlockConnect(char *err, const char *path)
|
||||
int anetUnixNonBlockConnect(char *err, char *path)
|
||||
{
|
||||
return anetUnixGenericConnect(err,path,ANET_CONNECT_NONBLOCK);
|
||||
}
|
||||
@@ -490,7 +476,7 @@ static int anetV6Only(char *err, int s) {
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog, int fReusePort, int fFirstListen)
|
||||
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog, int fReusePort)
|
||||
{
|
||||
int s = -1, rv;
|
||||
char _port[6]; /* strlen("65535") */
|
||||
@@ -512,12 +498,8 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
|
||||
|
||||
if (af == AF_INET6 && anetV6Only(err,s) == ANET_ERR) goto error;
|
||||
if (anetSetReuseAddr(err,s) == ANET_ERR) goto error;
|
||||
if (fReusePort && !fFirstListen && anetSetReusePort(err,s) == ANET_ERR) goto error;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) {
|
||||
s = ANET_ERR;
|
||||
goto end;
|
||||
}
|
||||
if (fReusePort && fFirstListen && anetSetReusePort(err,s) == ANET_ERR) goto error;
|
||||
if (fReusePort && anetSetReusePort(err,s) == ANET_ERR) goto error;
|
||||
if (anetListen(err,s,p->ai_addr,p->ai_addrlen,backlog) == ANET_ERR) s = ANET_ERR;
|
||||
goto end;
|
||||
}
|
||||
if (p == NULL) {
|
||||
@@ -533,14 +515,14 @@ end:
|
||||
return s;
|
||||
}
|
||||
|
||||
int anetTcpServer(char *err, int port, char *bindaddr, int backlog, int fReusePort, int fFirstListen)
|
||||
int anetTcpServer(char *err, int port, char *bindaddr, int backlog, int fReusePort)
|
||||
{
|
||||
return _anetTcpServer(err, port, bindaddr, AF_INET, backlog, fReusePort, fFirstListen);
|
||||
return _anetTcpServer(err, port, bindaddr, AF_INET, backlog, fReusePort);
|
||||
}
|
||||
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog, int fReusePort, int fFirstListen)
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog, int fReusePort)
|
||||
{
|
||||
return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog, fReusePort, fFirstListen);
|
||||
return _anetTcpServer(err, port, bindaddr, AF_INET6, backlog, fReusePort);
|
||||
}
|
||||
|
||||
int anetUnixServer(char *err, char *path, mode_t perm, int backlog)
|
||||
|
||||
+8
-9
@@ -53,17 +53,17 @@ extern "C" {
|
||||
#undef ip_len
|
||||
#endif
|
||||
|
||||
int anetTcpConnect(char *err, const char *addr, int port);
|
||||
int anetTcpNonBlockConnect(char *err, const char *addr, int port);
|
||||
int anetTcpNonBlockBindConnect(char *err, const char *addr, int port, const char *source_addr);
|
||||
int anetTcpNonBlockBestEffortBindConnect(char *err, const char *addr, int port, const char *source_addr);
|
||||
int anetUnixConnect(char *err, const char *path);
|
||||
int anetUnixNonBlockConnect(char *err, const char *path);
|
||||
int anetTcpConnect(char *err, char *addr, int port);
|
||||
int anetTcpNonBlockConnect(char *err, char *addr, int port);
|
||||
int anetTcpNonBlockBindConnect(char *err, char *addr, int port, char *source_addr);
|
||||
int anetTcpNonBlockBestEffortBindConnect(char *err, char *addr, int port, char *source_addr);
|
||||
int anetUnixConnect(char *err, char *path);
|
||||
int anetUnixNonBlockConnect(char *err, char *path);
|
||||
int anetRead(int fd, char *buf, int count);
|
||||
int anetResolve(char *err, char *host, char *ipbuf, size_t ipbuf_len);
|
||||
int anetResolveIP(char *err, char *host, char *ipbuf, size_t ipbuf_len);
|
||||
int anetTcpServer(char *err, int port, char *bindaddr, int backlog, int fReusePort, int fFirstListen);
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog, int fReusePort, int fFirstListen);
|
||||
int anetTcpServer(char *err, int port, char *bindaddr, int backlog, int fReusePort);
|
||||
int anetTcp6Server(char *err, int port, char *bindaddr, int backlog, int fReusePort);
|
||||
int anetUnixServer(char *err, char *path, mode_t perm, int backlog);
|
||||
int anetTcpAccept(char *err, int serversock, char *ip, size_t ip_len, int *port);
|
||||
int anetUnixAccept(char *err, int serversock);
|
||||
@@ -74,7 +74,6 @@ int anetEnableTcpNoDelay(char *err, int fd);
|
||||
int anetDisableTcpNoDelay(char *err, int fd);
|
||||
int anetTcpKeepAlive(char *err, int fd);
|
||||
int anetSendTimeout(char *err, int fd, long long ms);
|
||||
int anetRecvTimeout(char *err, int fd, long long ms);
|
||||
int anetPeerToString(int fd, char *ip, size_t ip_len, int *port);
|
||||
int anetKeepAlive(char *err, int fd, int interval);
|
||||
int anetSockName(int fd, char *ip, size_t ip_len, int *port);
|
||||
|
||||
+267
-413
File diff suppressed because it is too large.
Load diff
+1
-3
@@ -36,6 +36,4 @@ const char *ascii_logo =
|
||||
" Port: %d\n"
|
||||
" PID: %ld\n"
|
||||
" \n"
|
||||
" %s\n"
|
||||
" \n"
|
||||
" \n";
|
||||
" \n\n";
|
||||
+8
-22
@@ -4,7 +4,7 @@
|
||||
* Currently there is only a single operation, that is a background close(2)
|
||||
* system call. This is needed as when the process is the last owner of a
|
||||
* reference to a file closing it means unlinking it, and the deletion of the
|
||||
* file is slow, blocking the g_pserver->
|
||||
* file is slow, blocking the server.
|
||||
*
|
||||
* In the future we'll either continue implementing new things we need or
|
||||
* we'll switch to libeio. However there are probably long term uses for this
|
||||
@@ -85,7 +85,7 @@ struct bio_job {
|
||||
|
||||
void *bioProcessBackgroundJobs(void *arg);
|
||||
void lazyfreeFreeObjectFromBioThread(robj *o);
|
||||
void lazyfreeFreeDatabaseFromBioThread(dict *ht1, expireset *set);
|
||||
void lazyfreeFreeDatabaseFromBioThread(dict *ht1, dict *ht2);
|
||||
void lazyfreeFreeSlotsMapFromBioThread(rax *rt);
|
||||
|
||||
/* Make sure we have enough stack to perform all the things we do in the
|
||||
@@ -129,7 +129,7 @@ void bioInit(void) {
|
||||
}
|
||||
|
||||
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {
|
||||
struct bio_job *job = (bio_job*)zmalloc(sizeof(*job), MALLOC_LOCAL);
|
||||
struct bio_job *job = zmalloc(sizeof(*job), MALLOC_LOCAL);
|
||||
|
||||
job->time = time(NULL);
|
||||
job->arg1 = arg1;
|
||||
@@ -154,20 +154,6 @@ void *bioProcessBackgroundJobs(void *arg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case BIO_CLOSE_FILE:
|
||||
redis_set_thread_title("bio_close_file");
|
||||
break;
|
||||
case BIO_AOF_FSYNC:
|
||||
redis_set_thread_title("bio_aof_fsync");
|
||||
break;
|
||||
case BIO_LAZY_FREE:
|
||||
redis_set_thread_title("bio_lazy_free");
|
||||
break;
|
||||
}
|
||||
|
||||
redisSetCpuAffinity(g_pserver->bio_cpulist);
|
||||
|
||||
/* Make the thread killable at any time, so that bioKillThreads()
|
||||
* can work reliably. */
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
@@ -192,7 +178,7 @@ void *bioProcessBackgroundJobs(void *arg) {
|
||||
}
|
||||
/* Pop the job from the queue. */
|
||||
ln = listFirst(bio_jobs[type]);
|
||||
job = (bio_job*)ln->value;
|
||||
job = ln->value;
|
||||
/* It is now possible to unlock the background system as we know have
|
||||
* a stand alone job structure to process.*/
|
||||
pthread_mutex_unlock(&bio_mutex[type]);
|
||||
@@ -208,11 +194,11 @@ void *bioProcessBackgroundJobs(void *arg) {
|
||||
* arg2 & arg3 -> free two dictionaries (a Redis DB).
|
||||
* only arg3 -> free the skiplist. */
|
||||
if (job->arg1)
|
||||
lazyfreeFreeObjectFromBioThread((robj*)job->arg1);
|
||||
lazyfreeFreeObjectFromBioThread(job->arg1);
|
||||
else if (job->arg2 && job->arg3)
|
||||
lazyfreeFreeDatabaseFromBioThread((dict*)job->arg2,(expireset*)job->arg3);
|
||||
lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);
|
||||
else if (job->arg3)
|
||||
lazyfreeFreeSlotsMapFromBioThread((rax*)job->arg3);
|
||||
lazyfreeFreeSlotsMapFromBioThread(job->arg3);
|
||||
} else {
|
||||
serverPanic("Wrong job type in bioProcessBackgroundJobs().");
|
||||
}
|
||||
@@ -268,7 +254,7 @@ void bioKillThreads(void) {
|
||||
int err, j;
|
||||
|
||||
for (j = 0; j < BIO_NUM_OPS; j++) {
|
||||
if (bio_threads[j] && pthread_cancel(bio_threads[j]) == 0) {
|
||||
if (pthread_cancel(bio_threads[j]) == 0) {
|
||||
if ((err = pthread_join(bio_threads[j],NULL)) != 0) {
|
||||
serverLog(LL_WARNING,
|
||||
"Bio thread for job type #%d can be joined: %s",
|
||||
@@ -1,4 +1,3 @@
|
||||
#pragma once
|
||||
/*
|
||||
* Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
|
||||
+136
-236
@@ -37,9 +37,9 @@
|
||||
/* Count number of bits set in the binary array pointed by 's' and long
|
||||
* 'count' bytes. The implementation of this function is required to
|
||||
* work with a input string length up to 512 MB. */
|
||||
size_t redisPopcount(const void *s, long count) {
|
||||
size_t redisPopcount(void *s, long count) {
|
||||
size_t bits = 0;
|
||||
unsigned char *p = (unsigned char*)s;
|
||||
unsigned char *p = s;
|
||||
uint32_t *p4;
|
||||
static const unsigned char bitsinbyte[256] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8};
|
||||
|
||||
@@ -98,7 +98,7 @@ size_t redisPopcount(const void *s, long count) {
|
||||
* no zero bit is found, it returns count*8 assuming the string is zero
|
||||
* padded on the right. However if 'bit' is 1 it is possible that there is
|
||||
* not a single set bit in the bitmap. In this special case -1 is returned. */
|
||||
long redisBitpos(const void *s, unsigned long count, int bit) {
|
||||
long redisBitpos(void *s, unsigned long count, int bit) {
|
||||
unsigned long *l;
|
||||
unsigned char *c;
|
||||
unsigned long skipval, word = 0, one;
|
||||
@@ -269,7 +269,7 @@ int64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {
|
||||
* then zero is returned, otherwise in case of overflow, 1 is returned,
|
||||
* otherwise in case of underflow, -1 is returned.
|
||||
*
|
||||
* When non-zero is returned (overflow or underflow), if not NULL, *limit is
|
||||
* When non-zero is returned (oferflow or underflow), if not NULL, *limit is
|
||||
* set to the value the operation should result when an overflow happens,
|
||||
* depending on the specified overflow semantics:
|
||||
*
|
||||
@@ -396,8 +396,6 @@ void printBits(unsigned char *p, unsigned long count) {
|
||||
#define BITOP_OR 1
|
||||
#define BITOP_XOR 2
|
||||
#define BITOP_NOT 3
|
||||
#define BITOP_LSHIFT 4
|
||||
#define BITOP_RSHIFT 5
|
||||
|
||||
#define BITFIELDOP_GET 0
|
||||
#define BITFIELDOP_SET 1
|
||||
@@ -412,8 +410,8 @@ void printBits(unsigned char *p, unsigned long count) {
|
||||
* is multiplied by 'bits'. This is useful for the BITFIELD command. */
|
||||
int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int bits) {
|
||||
long long loffset;
|
||||
const char *err = "bit offset is not an integer or out of range";
|
||||
char *p = szFromObj(o);
|
||||
char *err = "bit offset is not an integer or out of range";
|
||||
char *p = ptrFromObj(o);
|
||||
size_t plen = sdslen(p);
|
||||
int usehash = 0;
|
||||
|
||||
@@ -447,8 +445,8 @@ int getBitOffsetFromArgument(client *c, robj *o, size_t *offset, int hash, int b
|
||||
*
|
||||
* On error C_ERR is returned and an error is sent to the client. */
|
||||
int getBitfieldTypeFromArgument(client *c, robj *o, int *sign, int *bits) {
|
||||
char *p = szFromObj(o);
|
||||
const char *err = "Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.";
|
||||
char *p = ptrFromObj(o);
|
||||
char *err = "Invalid bitfield type. Use something like i16 u8. Note that u64 is not supported but i64 is.";
|
||||
long long llbits;
|
||||
|
||||
if (p[0] == 'i') {
|
||||
@@ -487,7 +485,7 @@ robj *lookupStringForBitCommand(client *c, size_t maxbit) {
|
||||
} else {
|
||||
if (checkType(c,o,OBJ_STRING)) return NULL;
|
||||
o = dbUnshareStringValue(c->db,c->argv[1],o);
|
||||
o->m_ptr = sdsgrowzero(szFromObj(o),byte+1);
|
||||
o->m_ptr = sdsgrowzero(ptrFromObj(o),byte+1);
|
||||
}
|
||||
return o;
|
||||
}
|
||||
@@ -505,18 +503,18 @@ robj *lookupStringForBitCommand(client *c, size_t maxbit) {
|
||||
*
|
||||
* If the source object is NULL the function is guaranteed to return NULL
|
||||
* and set 'len' to 0. */
|
||||
const unsigned char *getObjectReadOnlyString(robj_roptr o, long *len, char *llbuf) {
|
||||
unsigned char *getObjectReadOnlyString(robj *o, long *len, char *llbuf) {
|
||||
serverAssert(o->type == OBJ_STRING);
|
||||
const unsigned char *p = NULL;
|
||||
unsigned char *p = NULL;
|
||||
|
||||
/* Set the 'p' pointer to the string, that can be just a stack allocated
|
||||
* array if our string was integer encoded. */
|
||||
if (o && o->encoding == OBJ_ENCODING_INT) {
|
||||
p = (const unsigned char*) llbuf;
|
||||
p = (unsigned char*) llbuf;
|
||||
if (len) *len = ll2string(llbuf,LONG_STR_SIZE,(long)ptrFromObj(o));
|
||||
} else if (o) {
|
||||
p = (const unsigned char*) ptrFromObj(o);
|
||||
if (len) *len = sdslen(szFromObj(o));
|
||||
p = (unsigned char*) ptrFromObj(o);
|
||||
if (len) *len = sdslen(ptrFromObj(o));
|
||||
} else {
|
||||
if (len) *len = 0;
|
||||
}
|
||||
@@ -526,7 +524,7 @@ const unsigned char *getObjectReadOnlyString(robj_roptr o, long *len, char *llbu
|
||||
/* SETBIT key offset bitvalue */
|
||||
void setbitCommand(client *c) {
|
||||
robj *o;
|
||||
const char *err = "bit is not an integer or out of range";
|
||||
char *err = "bit is not an integer or out of range";
|
||||
size_t bitoffset;
|
||||
ssize_t byte, bit;
|
||||
int byteval, bitval;
|
||||
@@ -556,15 +554,15 @@ void setbitCommand(client *c) {
|
||||
byteval &= ~(1 << bit);
|
||||
byteval |= ((on & 0x1) << bit);
|
||||
((uint8_t*)ptrFromObj(o))[byte] = byteval;
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
|
||||
g_pserver->dirty++;
|
||||
server.dirty++;
|
||||
addReply(c, bitval ? shared.cone : shared.czero);
|
||||
}
|
||||
|
||||
/* GETBIT key offset */
|
||||
void getbitCommand(client *c) {
|
||||
robj_roptr o;
|
||||
robj *o;
|
||||
char llbuf[32];
|
||||
size_t bitoffset;
|
||||
size_t byte, bit;
|
||||
@@ -573,13 +571,13 @@ void getbitCommand(client *c) {
|
||||
if (getBitOffsetFromArgument(c,c->argv[2],&bitoffset,0,0) != C_OK)
|
||||
return;
|
||||
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,OBJ_STRING)) return;
|
||||
|
||||
byte = bitoffset >> 3;
|
||||
bit = 7 - (bitoffset & 0x7);
|
||||
if (sdsEncodedObject(o)) {
|
||||
if (byte < sdslen(szFromObj(o)))
|
||||
if (byte < sdslen(ptrFromObj(o)))
|
||||
bitval = ((uint8_t*)ptrFromObj(o))[byte] & (1 << bit);
|
||||
} else {
|
||||
if (byte < (size_t)ll2string(llbuf,sizeof(llbuf),(long)ptrFromObj(o)))
|
||||
@@ -591,12 +589,10 @@ void getbitCommand(client *c) {
|
||||
|
||||
/* BITOP op_name target_key src_key1 src_key2 src_key3 ... src_keyN */
|
||||
void bitopCommand(client *c) {
|
||||
char *opname = szFromObj(c->argv[1]);
|
||||
robj *targetkey = c->argv[2];
|
||||
robj_roptr o;
|
||||
int op;
|
||||
unsigned long j, numkeys;
|
||||
robj_roptr *objects; /* Array of source objects. */
|
||||
char *opname = ptrFromObj(c->argv[1]);
|
||||
robj *o, *targetkey = c->argv[2];
|
||||
unsigned long op, j, numkeys;
|
||||
robj **objects; /* Array of source objects. */
|
||||
unsigned char **src; /* Array of source strings pointers. */
|
||||
unsigned long *len, maxlen = 0; /* Array of length of src strings,
|
||||
and max len. */
|
||||
@@ -612,10 +608,6 @@ void bitopCommand(client *c) {
|
||||
op = BITOP_XOR;
|
||||
else if((opname[0] == 'n' || opname[0] == 'N') && !strcasecmp(opname,"not"))
|
||||
op = BITOP_NOT;
|
||||
else if (!strcasecmp(opname, "lshift"))
|
||||
op = BITOP_LSHIFT;
|
||||
else if (!strcasecmp(opname, "rshift"))
|
||||
op = BITOP_RSHIFT;
|
||||
else {
|
||||
addReply(c,shared.syntaxerr);
|
||||
return;
|
||||
@@ -627,33 +619,16 @@ void bitopCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool fShiftOp = (op == BITOP_LSHIFT) || (op == BITOP_RSHIFT);
|
||||
long long shift = 0;
|
||||
|
||||
/* Sanity check: SHIFTS only accept a single arg and an integer */
|
||||
if (fShiftOp) {
|
||||
if (c->argc != 5) {
|
||||
addReplyError(c,"BITOP SHIFT must be called with a single source key and an integer shift.");
|
||||
return;
|
||||
}
|
||||
if (getLongLongFromObject(c->argv[4], &shift) != C_OK) {
|
||||
addReplyError(c, "BITOP SHIFT's last parameter must be an integer");
|
||||
return;
|
||||
}
|
||||
if (op == BITOP_RSHIFT)
|
||||
shift = -shift;
|
||||
}
|
||||
|
||||
/* Lookup keys, and store pointers to the string objects into an array. */
|
||||
numkeys = c->argc - (fShiftOp ? 4 : 3);
|
||||
src = (unsigned char**)zmalloc(sizeof(unsigned char*) * numkeys, MALLOC_LOCAL);
|
||||
len = (unsigned long*)zmalloc(sizeof(long) * numkeys, MALLOC_LOCAL);
|
||||
objects = (robj_roptr*)zmalloc(sizeof(robj_roptr) * numkeys, MALLOC_LOCAL);
|
||||
numkeys = c->argc - 3;
|
||||
src = zmalloc(sizeof(unsigned char*) * numkeys, MALLOC_LOCAL);
|
||||
len = zmalloc(sizeof(long) * numkeys, MALLOC_LOCAL);
|
||||
objects = zmalloc(sizeof(robj*) * numkeys, MALLOC_LOCAL);
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
o = lookupKeyRead(c->db,c->argv[j+3]);
|
||||
/* Handle non-existing keys as empty strings. */
|
||||
if (o == nullptr) {
|
||||
objects[j] = nullptr;
|
||||
if (o == NULL) {
|
||||
objects[j] = NULL;
|
||||
src[j] = NULL;
|
||||
len[j] = 0;
|
||||
minlen = 0;
|
||||
@@ -672,149 +647,100 @@ void bitopCommand(client *c) {
|
||||
return;
|
||||
}
|
||||
objects[j] = getDecodedObject(o);
|
||||
src[j] = (unsigned char*)ptrFromObj(objects[j]);
|
||||
len[j] = sdslen(szFromObj(objects[j]));
|
||||
src[j] = ptrFromObj(objects[j]);
|
||||
len[j] = sdslen(ptrFromObj(objects[j]));
|
||||
if (len[j] > maxlen) maxlen = len[j];
|
||||
if (j == 0 || len[j] < minlen) minlen = len[j];
|
||||
}
|
||||
|
||||
if (fShiftOp)
|
||||
{
|
||||
long newlen = (long)maxlen + shift/CHAR_BIT;
|
||||
if (shift > 0 && (shift % CHAR_BIT) != 0)
|
||||
newlen++;
|
||||
/* Compute the bit operation, if at least one string is not empty. */
|
||||
if (maxlen) {
|
||||
res = (unsigned char*) sdsnewlen(NULL,maxlen);
|
||||
unsigned char output, byte;
|
||||
unsigned long i;
|
||||
|
||||
if (newlen < 0)
|
||||
newlen = 0;
|
||||
|
||||
if (newlen)
|
||||
{
|
||||
res = (unsigned char*) sdsnewlen(NULL,newlen);
|
||||
if (shift >= 0)
|
||||
{ // left shift
|
||||
long byteoffset = shift/CHAR_BIT;
|
||||
memset(res, 0, byteoffset);
|
||||
long srcLen = newlen - byteoffset - ((shift % CHAR_BIT) ? 1 : 0);
|
||||
/* Fast path: as far as we have data for all the input bitmaps we
|
||||
* can take a fast path that performs much better than the
|
||||
* vanilla algorithm. On ARM we skip the fast path since it will
|
||||
* result in GCC compiling the code using multiple-words load/store
|
||||
* operations that are not supported even in ARM >= v6. */
|
||||
j = 0;
|
||||
#ifndef USE_ALIGNED_ACCESS
|
||||
if (minlen >= sizeof(unsigned long)*4 && numkeys <= 16) {
|
||||
unsigned long *lp[16];
|
||||
unsigned long *lres = (unsigned long*) res;
|
||||
|
||||
// now the bitshift+copy
|
||||
unsigned bitshift = shift % CHAR_BIT;
|
||||
unsigned char carry = 0;
|
||||
for (long iSrc = 0; iSrc < srcLen; ++iSrc)
|
||||
{
|
||||
res[byteoffset+iSrc] = (src[0][iSrc] << bitshift) | carry;
|
||||
carry = src[0][iSrc] >> (CHAR_BIT - bitshift);
|
||||
/* Note: sds pointer is always aligned to 8 byte boundary. */
|
||||
memcpy(lp,src,sizeof(unsigned long*)*numkeys);
|
||||
memcpy(res,src[0],minlen);
|
||||
|
||||
/* Different branches per different operations for speed (sorry). */
|
||||
if (op == BITOP_AND) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
lres[0] &= lp[i][0];
|
||||
lres[1] &= lp[i][1];
|
||||
lres[2] &= lp[i][2];
|
||||
lres[3] &= lp[i][3];
|
||||
lp[i]+=4;
|
||||
}
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
if (bitshift)
|
||||
res[newlen-1] = carry;
|
||||
}
|
||||
else
|
||||
{ // right shift
|
||||
long byteoffset = -shift/CHAR_BIT;
|
||||
unsigned bitshift = -shift % CHAR_BIT;
|
||||
if (bitshift)
|
||||
++byteoffset;
|
||||
res[0] = (src[0][byteoffset] << (CHAR_BIT-bitshift));
|
||||
if (byteoffset > 0)
|
||||
res[0] |= (src[0][byteoffset-1] >> bitshift);
|
||||
for (long idx = 1; idx < newlen; ++idx)
|
||||
{
|
||||
res[idx] = (src[0][byteoffset+idx] << (CHAR_BIT-bitshift)) | (src[0][byteoffset+idx-1] >> bitshift);
|
||||
} else if (op == BITOP_OR) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
lres[0] |= lp[i][0];
|
||||
lres[1] |= lp[i][1];
|
||||
lres[2] |= lp[i][2];
|
||||
lres[3] |= lp[i][3];
|
||||
lp[i]+=4;
|
||||
}
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
} else if (op == BITOP_XOR) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
lres[0] ^= lp[i][0];
|
||||
lres[1] ^= lp[i][1];
|
||||
lres[2] ^= lp[i][2];
|
||||
lres[3] ^= lp[i][3];
|
||||
lp[i]+=4;
|
||||
}
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
} else if (op == BITOP_NOT) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
lres[0] = ~lres[0];
|
||||
lres[1] = ~lres[1];
|
||||
lres[2] = ~lres[2];
|
||||
lres[3] = ~lres[3];
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
}
|
||||
}
|
||||
maxlen = newlen; // this is to ensure we DEL below if newlen was 0
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Compute the bit operation, if at least one string is not empty. */
|
||||
if (maxlen) {
|
||||
res = (unsigned char*) sdsnewlen(NULL,maxlen);
|
||||
unsigned char output, byte;
|
||||
unsigned long i;
|
||||
#endif
|
||||
|
||||
/* Fast path: as far as we have data for all the input bitmaps we
|
||||
* can take a fast path that performs much better than the
|
||||
* vanilla algorithm. On ARM we skip the fast path since it will
|
||||
* result in GCC compiling the code using multiple-words load/store
|
||||
* operations that are not supported even in ARM >= v6. */
|
||||
j = 0;
|
||||
#ifndef USE_ALIGNED_ACCESS
|
||||
if (minlen >= sizeof(unsigned long)*4 && numkeys <= 16) {
|
||||
unsigned long *lp[16];
|
||||
unsigned long *lres = (unsigned long*) res;
|
||||
|
||||
/* Note: sds pointer is always aligned to 8 byte boundary. */
|
||||
memcpy(lp,src,sizeof(unsigned long*)*numkeys);
|
||||
memcpy(res,src[0],minlen);
|
||||
|
||||
/* Different branches per different operations for speed (sorry). */
|
||||
if (op == BITOP_AND) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
lres[0] &= lp[i][0];
|
||||
lres[1] &= lp[i][1];
|
||||
lres[2] &= lp[i][2];
|
||||
lres[3] &= lp[i][3];
|
||||
lp[i]+=4;
|
||||
}
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
} else if (op == BITOP_OR) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
lres[0] |= lp[i][0];
|
||||
lres[1] |= lp[i][1];
|
||||
lres[2] |= lp[i][2];
|
||||
lres[3] |= lp[i][3];
|
||||
lp[i]+=4;
|
||||
}
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
} else if (op == BITOP_XOR) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
lres[0] ^= lp[i][0];
|
||||
lres[1] ^= lp[i][1];
|
||||
lres[2] ^= lp[i][2];
|
||||
lres[3] ^= lp[i][3];
|
||||
lp[i]+=4;
|
||||
}
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
} else if (op == BITOP_NOT) {
|
||||
while(minlen >= sizeof(unsigned long)*4) {
|
||||
lres[0] = ~lres[0];
|
||||
lres[1] = ~lres[1];
|
||||
lres[2] = ~lres[2];
|
||||
lres[3] = ~lres[3];
|
||||
lres+=4;
|
||||
j += sizeof(unsigned long)*4;
|
||||
minlen -= sizeof(unsigned long)*4;
|
||||
}
|
||||
/* j is set to the next byte to process by the previous loop. */
|
||||
for (; j < maxlen; j++) {
|
||||
output = (len[0] <= j) ? 0 : src[0][j];
|
||||
if (op == BITOP_NOT) output = ~output;
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
byte = (len[i] <= j) ? 0 : src[i][j];
|
||||
switch(op) {
|
||||
case BITOP_AND: output &= byte; break;
|
||||
case BITOP_OR: output |= byte; break;
|
||||
case BITOP_XOR: output ^= byte; break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* j is set to the next byte to process by the previous loop. */
|
||||
for (; j < maxlen; j++) {
|
||||
output = (len[0] <= j) ? 0 : src[0][j];
|
||||
if (op == BITOP_NOT) output = ~output;
|
||||
for (i = 1; i < numkeys; i++) {
|
||||
byte = (len[i] <= j) ? 0 : src[i][j];
|
||||
switch(op) {
|
||||
case BITOP_AND: output &= byte; break;
|
||||
case BITOP_OR: output |= byte; break;
|
||||
case BITOP_XOR: output ^= byte; break;
|
||||
}
|
||||
}
|
||||
res[j] = output;
|
||||
}
|
||||
res[j] = output;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
@@ -827,27 +753,27 @@ void bitopCommand(client *c) {
|
||||
|
||||
/* Store the computed value into the target key */
|
||||
if (maxlen) {
|
||||
robj *o = createObject(OBJ_STRING,res);
|
||||
setKey(c,c->db,targetkey,o);
|
||||
o = createObject(OBJ_STRING,res);
|
||||
setKey(c->db,targetkey,o);
|
||||
notifyKeyspaceEvent(NOTIFY_STRING,"set",targetkey,c->db->id);
|
||||
decrRefCount(o);
|
||||
} else if (dbDelete(c->db,targetkey)) {
|
||||
signalModifiedKey(c,c->db,targetkey);
|
||||
signalModifiedKey(c->db,targetkey);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",targetkey,c->db->id);
|
||||
}
|
||||
g_pserver->dirty++;
|
||||
server.dirty++;
|
||||
addReplyLongLong(c,maxlen); /* Return the output string length in bytes. */
|
||||
}
|
||||
|
||||
/* BITCOUNT key [start end] */
|
||||
void bitcountCommand(client *c) {
|
||||
robj_roptr o;
|
||||
robj *o;
|
||||
long start, end, strlen;
|
||||
const unsigned char *p;
|
||||
unsigned char *p;
|
||||
char llbuf[LONG_STR_SIZE];
|
||||
|
||||
/* Lookup, check for type, and return 0 for non existing keys. */
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == nullptr ||
|
||||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
|
||||
checkType(c,o,OBJ_STRING)) return;
|
||||
p = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
@@ -890,9 +816,9 @@ void bitcountCommand(client *c) {
|
||||
|
||||
/* BITPOS key bit [start [end]] */
|
||||
void bitposCommand(client *c) {
|
||||
robj_roptr o;
|
||||
robj *o;
|
||||
long bit, start, end, strlen;
|
||||
const unsigned char *p;
|
||||
unsigned char *p;
|
||||
char llbuf[LONG_STR_SIZE];
|
||||
int end_given = 0;
|
||||
|
||||
@@ -908,7 +834,7 @@ void bitposCommand(client *c) {
|
||||
/* If the key does not exist, from our point of view it is an infinite
|
||||
* array of 0 bits. If the user is looking for the fist clear bit return 0,
|
||||
* If the user is looking for the first set bit, return -1. */
|
||||
if ((o = lookupKeyRead(c->db,c->argv[1])) == nullptr) {
|
||||
if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
|
||||
addReplyLongLong(c, bit ? -1 : 0);
|
||||
return;
|
||||
}
|
||||
@@ -976,9 +902,6 @@ void bitposCommand(client *c) {
|
||||
* OVERFLOW [WRAP|SAT|FAIL]
|
||||
*/
|
||||
|
||||
#define BITFIELD_FLAG_NONE 0
|
||||
#define BITFIELD_FLAG_READONLY (1<<0)
|
||||
|
||||
struct bitfieldOp {
|
||||
uint64_t offset; /* Bitfield offset. */
|
||||
int64_t i64; /* Increment amount (INCRBY) or SET value */
|
||||
@@ -988,11 +911,8 @@ struct bitfieldOp {
|
||||
int sign; /* True if signed, otherwise unsigned op. */
|
||||
};
|
||||
|
||||
/* This implements both the BITFIELD command and the BITFIELD_RO command
|
||||
* when flags is set to BITFIELD_FLAG_READONLY: in this case only the
|
||||
* GET subcommand is allowed, other subcommands will return an error. */
|
||||
void bitfieldGeneric(client *c, int flags) {
|
||||
robj_roptr o;
|
||||
void bitfieldCommand(client *c) {
|
||||
robj *o;
|
||||
size_t bitoffset;
|
||||
int j, numops = 0, changes = 0;
|
||||
struct bitfieldOp *ops = NULL; /* Array of ops to execute at end. */
|
||||
@@ -1002,7 +922,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
|
||||
for (j = 2; j < c->argc; j++) {
|
||||
int remargs = c->argc-j-1; /* Remaining args other than current. */
|
||||
char *subcmd = szFromObj(c->argv[j]); /* Current command name. */
|
||||
char *subcmd = ptrFromObj(c->argv[j]); /* Current command name. */
|
||||
int opcode; /* Current operation code. */
|
||||
long long i64 = 0; /* Signed SET value. */
|
||||
int sign = 0; /* Signed or unsigned type? */
|
||||
@@ -1015,7 +935,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
else if (!strcasecmp(subcmd,"incrby") && remargs >= 3)
|
||||
opcode = BITFIELDOP_INCRBY;
|
||||
else if (!strcasecmp(subcmd,"overflow") && remargs >= 1) {
|
||||
char *owtypename = szFromObj(c->argv[j+1]);
|
||||
char *owtypename = ptrFromObj(c->argv[j+1]);
|
||||
j++;
|
||||
if (!strcasecmp(owtypename,"wrap"))
|
||||
owtype = BFOVERFLOW_WRAP;
|
||||
@@ -1058,7 +978,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
}
|
||||
|
||||
/* Populate the array of operations we'll process. */
|
||||
ops = (bitfieldOp*)zrealloc(ops,sizeof(*ops)*(numops+1), MALLOC_SHARED);
|
||||
ops = zrealloc(ops,sizeof(*ops)*(numops+1), MALLOC_SHARED);
|
||||
ops[numops].offset = bitoffset;
|
||||
ops[numops].i64 = i64;
|
||||
ops[numops].opcode = opcode;
|
||||
@@ -1074,24 +994,12 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
/* Lookup for read is ok if key doesn't exit, but errors
|
||||
* if it's not a string. */
|
||||
o = lookupKeyRead(c->db,c->argv[1]);
|
||||
if (o != nullptr && checkType(c,o,OBJ_STRING)) {
|
||||
zfree(ops);
|
||||
return;
|
||||
}
|
||||
if (o != NULL && checkType(c,o,OBJ_STRING)) return;
|
||||
} else {
|
||||
if (flags & BITFIELD_FLAG_READONLY) {
|
||||
zfree(ops);
|
||||
addReplyError(c, "BITFIELD_RO only supports the GET subcommand");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lookup by making room up to the farest bit reached by
|
||||
* this operation. */
|
||||
if ((o = lookupStringForBitCommand(c,
|
||||
highest_write_offset)) == nullptr) {
|
||||
zfree(ops);
|
||||
return;
|
||||
}
|
||||
highest_write_offset)) == NULL) return;
|
||||
}
|
||||
|
||||
addReplyArrayLen(c,numops);
|
||||
@@ -1115,7 +1023,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
int64_t oldval, newval, wrapped, retval;
|
||||
int overflow;
|
||||
|
||||
oldval = getSignedBitfield((unsigned char*)ptrFromObj(o),thisop->offset,
|
||||
oldval = getSignedBitfield(ptrFromObj(o),thisop->offset,
|
||||
thisop->bits);
|
||||
|
||||
if (thisop->opcode == BITFIELDOP_INCRBY) {
|
||||
@@ -1136,7 +1044,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
* NULL to signal the condition. */
|
||||
if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {
|
||||
addReplyLongLong(c,retval);
|
||||
setSignedBitfield((unsigned char*)ptrFromObj(o),thisop->offset,
|
||||
setSignedBitfield(ptrFromObj(o),thisop->offset,
|
||||
thisop->bits,newval);
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
@@ -1145,7 +1053,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
uint64_t oldval, newval, wrapped, retval;
|
||||
int overflow;
|
||||
|
||||
oldval = getUnsignedBitfield((unsigned char*)ptrFromObj(o),thisop->offset,
|
||||
oldval = getUnsignedBitfield(ptrFromObj(o),thisop->offset,
|
||||
thisop->bits);
|
||||
|
||||
if (thisop->opcode == BITFIELDOP_INCRBY) {
|
||||
@@ -1165,7 +1073,7 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
* NULL to signal the condition. */
|
||||
if (!(overflow && thisop->owtype == BFOVERFLOW_FAIL)) {
|
||||
addReplyLongLong(c,retval);
|
||||
setUnsignedBitfield((unsigned char*)ptrFromObj(o),thisop->offset,
|
||||
setUnsignedBitfield(ptrFromObj(o),thisop->offset,
|
||||
thisop->bits,newval);
|
||||
} else {
|
||||
addReplyNull(c);
|
||||
@@ -1176,10 +1084,10 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
/* GET */
|
||||
unsigned char buf[9];
|
||||
long strlen = 0;
|
||||
const unsigned char *src = NULL;
|
||||
unsigned char *src = NULL;
|
||||
char llbuf[LONG_STR_SIZE];
|
||||
|
||||
if (o != nullptr)
|
||||
if (o != NULL)
|
||||
src = getObjectReadOnlyString(o,&strlen,llbuf);
|
||||
|
||||
/* For GET we use a trick: before executing the operation
|
||||
@@ -1209,17 +1117,9 @@ void bitfieldGeneric(client *c, int flags) {
|
||||
}
|
||||
|
||||
if (changes) {
|
||||
signalModifiedKey(c,c->db,c->argv[1]);
|
||||
signalModifiedKey(c->db,c->argv[1]);
|
||||
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
|
||||
g_pserver->dirty += changes;
|
||||
server.dirty += changes;
|
||||
}
|
||||
zfree(ops);
|
||||
}
|
||||
|
||||
void bitfieldCommand(client *c) {
|
||||
bitfieldGeneric(c, BITFIELD_FLAG_NONE);
|
||||
}
|
||||
|
||||
void bitfieldroCommand(client *c) {
|
||||
bitfieldGeneric(c, BITFIELD_FLAG_READONLY);
|
||||
}
|
||||
+282
-326
@@ -31,6 +31,9 @@
|
||||
*
|
||||
* API:
|
||||
*
|
||||
* getTimeoutFromObjectOrReply() is just an utility function to parse a
|
||||
* timeout argument since blocking operations usually require a timeout.
|
||||
*
|
||||
* blockClient() set the CLIENT_BLOCKED flag in the client, and set the
|
||||
* specified block type 'btype' filed to one of BLOCKED_* macros.
|
||||
*
|
||||
@@ -61,24 +64,44 @@
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include <mutex>
|
||||
|
||||
int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);
|
||||
|
||||
/* This structure represents the blocked key information that we store
|
||||
* in the client structure. Each client blocked on keys, has a
|
||||
* client->bpop.keys hash table. The keys of the hash table are Redis
|
||||
* keys pointers to 'robj' structures. The value is this structure.
|
||||
* The structure has two goals: firstly we store the list node that this
|
||||
* client uses to be listed in the database "blocked clients for this key"
|
||||
* list, so we can later unblock in O(1) without a list scan.
|
||||
* Secondly for certain blocking types, we have additional info. Right now
|
||||
* the only use for additional info we have is when clients are blocked
|
||||
* on streams, as we have to remember the ID it blocked for. */
|
||||
typedef struct bkinfo {
|
||||
listNode *listnode; /* List node for db->blocking_keys[key] list. */
|
||||
streamID stream_id; /* Stream ID if we blocked in a stream. */
|
||||
} bkinfo;
|
||||
/* Get a timeout value from an object and store it into 'timeout'.
|
||||
* The final timeout is always stored as milliseconds as a time where the
|
||||
* timeout will expire, however the parsing is performed according to
|
||||
* the 'unit' that can be seconds or milliseconds.
|
||||
*
|
||||
* Note that if the timeout is zero (usually from the point of view of
|
||||
* commands API this means no timeout) the value stored into 'timeout'
|
||||
* is zero. */
|
||||
int getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit) {
|
||||
long long tval;
|
||||
long double ftval;
|
||||
|
||||
if (unit == UNIT_SECONDS) {
|
||||
if (getLongDoubleFromObjectOrReply(c,object,&ftval,
|
||||
"timeout is not an float or out of range") != C_OK)
|
||||
return C_ERR;
|
||||
tval = (long long) (ftval * 1000.0);
|
||||
} else {
|
||||
if (getLongLongFromObjectOrReply(c,object,&tval,
|
||||
"timeout is not an integer or out of range") != C_OK)
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
if (tval < 0) {
|
||||
addReplyError(c,"timeout is negative");
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
if (tval > 0) {
|
||||
tval += mstime();
|
||||
}
|
||||
*timeout = tval;
|
||||
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Block a client for the specific operation type. Once the CLIENT_BLOCKED
|
||||
* flag is set client query buffer is not longer processed, but accumulated,
|
||||
@@ -87,9 +110,8 @@ void blockClient(client *c, int btype) {
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
c->flags |= CLIENT_BLOCKED;
|
||||
c->btype = btype;
|
||||
g_pserver->blocked_clients++;
|
||||
g_pserver->blocked_clients_by_type[btype]++;
|
||||
addClientToTimeoutTable(c);
|
||||
server.blocked_clients++;
|
||||
server.blocked_clients_by_type[btype]++;
|
||||
}
|
||||
|
||||
/* This function is called in the beforeSleep() function of the event loop
|
||||
@@ -100,13 +122,13 @@ void processUnblockedClients(int iel) {
|
||||
|
||||
listNode *ln;
|
||||
client *c;
|
||||
list *unblocked_clients = g_pserver->rgthreadvar[iel].unblocked_clients;
|
||||
serverAssert(iel == (serverTL - g_pserver->rgthreadvar));
|
||||
list *unblocked_clients = server.rgthreadvar[iel].unblocked_clients;
|
||||
serverAssert(iel == (serverTL - server.rgthreadvar));
|
||||
|
||||
while (listLength(unblocked_clients)) {
|
||||
ln = listFirst(unblocked_clients);
|
||||
serverAssert(ln != NULL);
|
||||
c = (client*)ln->value;
|
||||
c = ln->value;
|
||||
listDelNode(unblocked_clients,ln);
|
||||
AssertCorrectThread(c);
|
||||
|
||||
@@ -119,7 +141,7 @@ void processUnblockedClients(int iel) {
|
||||
* the code is conceptually more correct this way. */
|
||||
if (!(c->flags & CLIENT_BLOCKED)) {
|
||||
if (c->querybuf && sdslen(c->querybuf) > 0) {
|
||||
processInputBuffer(c, CMD_CALL_FULL);
|
||||
processInputBufferAndReplicate(c);
|
||||
}
|
||||
}
|
||||
fastlock_unlock(&c->lock);
|
||||
@@ -149,7 +171,7 @@ void queueClientForReprocessing(client *c) {
|
||||
fastlock_lock(&c->lock);
|
||||
if (!(c->flags & CLIENT_UNBLOCKED)) {
|
||||
c->flags |= CLIENT_UNBLOCKED;
|
||||
listAddNodeTail(g_pserver->rgthreadvar[c->iel].unblocked_clients,c);
|
||||
listAddNodeTail(server.rgthreadvar[c->iel].unblocked_clients,c);
|
||||
}
|
||||
fastlock_unlock(&c->lock);
|
||||
}
|
||||
@@ -158,7 +180,6 @@ void queueClientForReprocessing(client *c) {
|
||||
* of operation the client is blocking for. */
|
||||
void unblockClient(client *c) {
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
serverAssert(c->lock.fOwnLock());
|
||||
if (c->btype == BLOCKED_LIST ||
|
||||
c->btype == BLOCKED_ZSET ||
|
||||
c->btype == BLOCKED_STREAM) {
|
||||
@@ -166,18 +187,16 @@ void unblockClient(client *c) {
|
||||
} else if (c->btype == BLOCKED_WAIT) {
|
||||
unblockClientWaitingReplicas(c);
|
||||
} else if (c->btype == BLOCKED_MODULE) {
|
||||
if (moduleClientIsBlockedOnKeys(c)) unblockClientWaitingData(c);
|
||||
unblockClientFromModule(c);
|
||||
} else {
|
||||
serverPanic("Unknown btype in unblockClient().");
|
||||
}
|
||||
/* Clear the flags, and put the client in the unblocked list so that
|
||||
* we'll process new commands in its query buffer ASAP. */
|
||||
g_pserver->blocked_clients--;
|
||||
g_pserver->blocked_clients_by_type[c->btype]--;
|
||||
server.blocked_clients--;
|
||||
server.blocked_clients_by_type[c->btype]--;
|
||||
c->flags &= ~CLIENT_BLOCKED;
|
||||
c->btype = BLOCKED_NONE;
|
||||
removeClientFromTimeoutTable(c);
|
||||
queueClientForReprocessing(c);
|
||||
}
|
||||
|
||||
@@ -188,9 +207,9 @@ void replyToBlockedClientTimedOut(client *c) {
|
||||
if (c->btype == BLOCKED_LIST ||
|
||||
c->btype == BLOCKED_ZSET ||
|
||||
c->btype == BLOCKED_STREAM) {
|
||||
addReplyNullArrayAsync(c);
|
||||
addReplyNullArray(c);
|
||||
} else if (c->btype == BLOCKED_WAIT) {
|
||||
addReplyLongLongAsync(c,replicationCountAcksByOffset(c->bpop.reploffset));
|
||||
addReplyLongLong(c,replicationCountAcksByOffset(c->bpop.reploffset));
|
||||
} else if (c->btype == BLOCKED_MODULE) {
|
||||
moduleBlockedClientTimedOut(c);
|
||||
} else {
|
||||
@@ -200,8 +219,8 @@ void replyToBlockedClientTimedOut(client *c) {
|
||||
|
||||
/* Mass-unblock clients because something changed in the instance that makes
|
||||
* blocking no longer safe. For example clients blocked in list operations
|
||||
* in an instance which turns from master to replica is unsafe, so this function
|
||||
* is called when a master turns into a replica.
|
||||
* in an instance which turns from master to slave is unsafe, so this function
|
||||
* is called when a master turns into a slave.
|
||||
*
|
||||
* The semantics is to send an -UNBLOCKED error to the client, disconnecting
|
||||
* it at the same time. */
|
||||
@@ -210,9 +229,9 @@ void disconnectAllBlockedClients(void) {
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
|
||||
listRewind(g_pserver->clients,&li);
|
||||
listRewind(server.clients,&li);
|
||||
while((ln = listNext(&li))) {
|
||||
client *c = (client*)listNodeValue(ln);
|
||||
client *c = listNodeValue(ln);
|
||||
|
||||
fastlock_lock(&c->lock);
|
||||
if (c->flags & CLIENT_BLOCKED) {
|
||||
@@ -226,251 +245,6 @@ void disconnectAllBlockedClients(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for handleClientsBlockedOnKeys(). This function is called
|
||||
* when there may be clients blocked on a list key, and there may be new
|
||||
* data to fetch (the key is ready). */
|
||||
void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = (list*)dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
|
||||
while(numclients--) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = (client*)clientnode->value;
|
||||
std::unique_lock<decltype(receiver->lock)> lock(receiver->lock);
|
||||
|
||||
if (receiver->btype != BLOCKED_LIST) {
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again. */
|
||||
listRotateHeadToTail(clients);
|
||||
continue;
|
||||
}
|
||||
|
||||
robj *dstkey = receiver->bpop.target;
|
||||
int where = (receiver->lastcmd &&
|
||||
receiver->lastcmd->proc == blpopCommand) ?
|
||||
LIST_HEAD : LIST_TAIL;
|
||||
robj *value = listTypePop(o,where);
|
||||
|
||||
if (value) {
|
||||
/* Protect receiver->bpop.target, that will be
|
||||
* freed by the next unblockClient()
|
||||
* call. */
|
||||
if (dstkey) incrRefCount(dstkey);
|
||||
unblockClient(receiver);
|
||||
|
||||
if (serveClientBlockedOnList(receiver,
|
||||
rl->key,dstkey,rl->db,value,
|
||||
where) == C_ERR)
|
||||
{
|
||||
/* If we failed serving the client we need
|
||||
* to also undo the POP operation. */
|
||||
listTypePush(o,value,where);
|
||||
}
|
||||
|
||||
if (dstkey) decrRefCount(dstkey);
|
||||
decrRefCount(value);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listTypeLength(o) == 0) {
|
||||
dbDelete(rl->db,rl->key);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",rl->key,rl->db->id);
|
||||
}
|
||||
/* We don't call signalModifiedKey() as it was already called
|
||||
* when an element was pushed on the list. */
|
||||
}
|
||||
|
||||
/* Helper function for handleClientsBlockedOnKeys(). This function is called
|
||||
* when there may be clients blocked on a sorted set key, and there may be new
|
||||
* data to fetch (the key is ready). */
|
||||
void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = (list*)dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
unsigned long zcard = zsetLength(o);
|
||||
|
||||
while(numclients-- && zcard) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = (client*)clientnode->value;
|
||||
std::unique_lock<decltype(receiver->lock)> lock(receiver->lock);
|
||||
|
||||
if (receiver->btype != BLOCKED_ZSET) {
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again. */
|
||||
listRotateHeadToTail(clients);
|
||||
continue;
|
||||
}
|
||||
|
||||
int where = (receiver->lastcmd &&
|
||||
receiver->lastcmd->proc == bzpopminCommand)
|
||||
? ZSET_MIN : ZSET_MAX;
|
||||
unblockClient(receiver);
|
||||
genericZpopCommand(receiver,&rl->key,1,where,1,NULL);
|
||||
zcard--;
|
||||
|
||||
/* Replicate the command. */
|
||||
robj *argv[2];
|
||||
struct redisCommand *cmd = where == ZSET_MIN ?
|
||||
cserver.zpopminCommand :
|
||||
cserver.zpopmaxCommand;
|
||||
argv[0] = createStringObject(cmd->name,strlen(cmd->name));
|
||||
argv[1] = rl->key;
|
||||
incrRefCount(rl->key);
|
||||
propagate(cmd,receiver->db->id,
|
||||
argv,2,PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for handleClientsBlockedOnKeys(). This function is called
|
||||
* when there may be clients blocked on a stream key, and there may be new
|
||||
* data to fetch (the key is ready). */
|
||||
void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
stream *s = (stream*)ptrFromObj(o);
|
||||
|
||||
/* We need to provide the new data arrived on the stream
|
||||
* to all the clients that are waiting for an offset smaller
|
||||
* than the current top item. */
|
||||
if (de) {
|
||||
list *clients = (list*)dictGetVal(de);
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
listRewind(clients,&li);
|
||||
|
||||
while((ln = listNext(&li))) {
|
||||
client *receiver = (client*)listNodeValue(ln);
|
||||
if (receiver->btype != BLOCKED_STREAM) continue;
|
||||
std::unique_lock<decltype(receiver->lock)> lock(receiver->lock);
|
||||
bkinfo *bki = (bkinfo*)dictFetchValue(receiver->bpop.keys,rl->key);
|
||||
streamID *gt = &bki->stream_id;
|
||||
|
||||
/* If we blocked in the context of a consumer
|
||||
* group, we need to resolve the group and update the
|
||||
* last ID the client is blocked for: this is needed
|
||||
* because serving other clients in the same consumer
|
||||
* group will alter the "last ID" of the consumer
|
||||
* group, and clients blocked in a consumer group are
|
||||
* always blocked for the ">" ID: we need to deliver
|
||||
* only new messages and avoid unblocking the client
|
||||
* otherwise. */
|
||||
streamCG *group = NULL;
|
||||
if (receiver->bpop.xread_group) {
|
||||
group = streamLookupCG(s,
|
||||
szFromObj(receiver->bpop.xread_group));
|
||||
/* If the group was not found, send an error
|
||||
* to the consumer. */
|
||||
if (!group) {
|
||||
addReplyErrorAsync(receiver,
|
||||
"-NOGROUP the consumer group this client "
|
||||
"was blocked on no longer exists");
|
||||
unblockClient(receiver);
|
||||
continue;
|
||||
} else {
|
||||
*gt = group->last_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (streamCompareID(&s->last_id, gt) > 0) {
|
||||
streamID start = *gt;
|
||||
streamIncrID(&start);
|
||||
|
||||
/* Lookup the consumer for the group, if any. */
|
||||
streamConsumer *consumer = NULL;
|
||||
int noack = 0;
|
||||
|
||||
if (group) {
|
||||
consumer =
|
||||
streamLookupConsumer(group,
|
||||
szFromObj(receiver->bpop.xread_consumer),
|
||||
SLC_NONE);
|
||||
noack = receiver->bpop.xread_group_noack;
|
||||
}
|
||||
|
||||
/* Emit the two elements sub-array consisting of
|
||||
* the name of the stream and the data we
|
||||
* extracted from it. Wrapped in a single-item
|
||||
* array, since we have just one key. */
|
||||
if (receiver->resp == 2) {
|
||||
addReplyArrayLenAsync(receiver,1);
|
||||
addReplyArrayLenAsync(receiver,2);
|
||||
} else {
|
||||
addReplyMapLenAsync(receiver,1);
|
||||
}
|
||||
addReplyBulkAsync(receiver,rl->key);
|
||||
|
||||
streamPropInfo pi = {
|
||||
rl->key,
|
||||
receiver->bpop.xread_group
|
||||
};
|
||||
streamReplyWithRange(receiver,s,&start,NULL,
|
||||
receiver->bpop.xread_count,
|
||||
0, group, consumer, noack, &pi);
|
||||
|
||||
/* Note that after we unblock the client, 'gt'
|
||||
* and other receiver->bpop stuff are no longer
|
||||
* valid, so we must do the setup above before
|
||||
* this call. */
|
||||
unblockClient(receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function for handleClientsBlockedOnKeys(). This function is called
|
||||
* in order to check if we can serve clients blocked by modules using
|
||||
* RM_BlockClientOnKeys(), when the corresponding key was signaled as ready:
|
||||
* our goal here is to call the RedisModuleBlockedClient reply() callback to
|
||||
* see if the key is really able to serve the client, and in that case,
|
||||
* unblock it. */
|
||||
void serveClientsBlockedOnKeyByModule(readyList *rl) {
|
||||
dictEntry *de;
|
||||
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = (list*)dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
|
||||
while(numclients--) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = (client*)clientnode->value;
|
||||
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again: clients here may not be
|
||||
* ready to be served, so they'll remain in the list
|
||||
* sometimes. We want also be able to skip clients that are
|
||||
* not blocked for the MODULE type safely. */
|
||||
listRotateHeadToTail(clients);
|
||||
|
||||
if (receiver->btype != BLOCKED_MODULE) continue;
|
||||
|
||||
/* Note that if *this* client cannot be served by this key,
|
||||
* it does not mean that another client that is next into the
|
||||
* list cannot be served as well: they may be blocked by
|
||||
* different modules with different triggers to consider if a key
|
||||
* is ready or not. This means we can't exit the loop but need
|
||||
* to continue after the first failure. */
|
||||
if (!moduleTryServeClientBlockedOnKey(receiver, rl->key)) continue;
|
||||
|
||||
moduleUnblockClient(receiver);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function should be called by Redis every time a single command,
|
||||
* a MULTI/EXEC block, or a Lua script, terminated its execution after
|
||||
* being called by a client. It handles serving clients blocked in
|
||||
@@ -478,7 +252,7 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
|
||||
*
|
||||
* All the keys with at least one client blocked that received at least
|
||||
* one new element via some write operation are accumulated into
|
||||
* the g_pserver->ready_keys list. This function will run the list and will
|
||||
* the server.ready_keys list. This function will run the list and will
|
||||
* serve clients accordingly. Note that the function will iterate again and
|
||||
* again as a result of serving BRPOPLPUSH we can have new blocking clients
|
||||
* to serve because of the PUSH side of BRPOPLPUSH.
|
||||
@@ -494,50 +268,232 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
|
||||
* do, the function is already fair. */
|
||||
void handleClientsBlockedOnKeys(void) {
|
||||
serverAssert(GlobalLocksAcquired());
|
||||
while(listLength(g_pserver->ready_keys) != 0) {
|
||||
while(listLength(server.ready_keys) != 0) {
|
||||
list *l;
|
||||
|
||||
/* Point g_pserver->ready_keys to a fresh list and save the current one
|
||||
/* Point server.ready_keys to a fresh list and save the current one
|
||||
* locally. This way as we run the old list we are free to call
|
||||
* signalKeyAsReady() that may push new elements in g_pserver->ready_keys
|
||||
* signalKeyAsReady() that may push new elements in server.ready_keys
|
||||
* when handling clients blocked into BRPOPLPUSH. */
|
||||
l = g_pserver->ready_keys;
|
||||
g_pserver->ready_keys = listCreate();
|
||||
l = server.ready_keys;
|
||||
server.ready_keys = listCreate();
|
||||
|
||||
while(listLength(l) != 0) {
|
||||
listNode *ln = listFirst(l);
|
||||
readyList *rl = (readyList*)ln->value;
|
||||
readyList *rl = ln->value;
|
||||
|
||||
/* First of all remove this key from db->ready_keys so that
|
||||
* we can safely call signalKeyAsReady() against this key. */
|
||||
dictDelete(rl->db->ready_keys,rl->key);
|
||||
|
||||
/* Even if we are not inside call(), increment the call depth
|
||||
* in order to make sure that keys are expired against a fixed
|
||||
* reference time, and not against the wallclock time. This
|
||||
* way we can lookup an object multiple times (BRPOPLPUSH does
|
||||
* that) without the risk of it being freed in the second
|
||||
* lookup, invalidating the first one.
|
||||
* See https://github.com/antirez/redis/pull/6554. */
|
||||
serverTL->fixed_time_expire++;
|
||||
updateCachedTime(0);
|
||||
|
||||
/* Serve clients blocked on list key. */
|
||||
robj *o = lookupKeyWrite(rl->db,rl->key);
|
||||
if (o != NULL && o->type == OBJ_LIST) {
|
||||
dictEntry *de;
|
||||
|
||||
if (o != NULL) {
|
||||
if (o->type == OBJ_LIST)
|
||||
serveClientsBlockedOnListKey(o,rl);
|
||||
else if (o->type == OBJ_ZSET)
|
||||
serveClientsBlockedOnSortedSetKey(o,rl);
|
||||
else if (o->type == OBJ_STREAM)
|
||||
serveClientsBlockedOnStreamKey(o,rl);
|
||||
/* We want to serve clients blocked on module keys
|
||||
* regardless of the object type: we don't know what the
|
||||
* module is trying to accomplish right now. */
|
||||
serveClientsBlockedOnKeyByModule(rl);
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
|
||||
while(numclients--) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = clientnode->value;
|
||||
|
||||
if (receiver->btype != BLOCKED_LIST) {
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again. */
|
||||
listDelNode(clients,clientnode);
|
||||
listAddNodeTail(clients,receiver);
|
||||
continue;
|
||||
}
|
||||
|
||||
robj *dstkey = receiver->bpop.target;
|
||||
int where = (receiver->lastcmd &&
|
||||
receiver->lastcmd->proc == blpopCommand) ?
|
||||
LIST_HEAD : LIST_TAIL;
|
||||
robj *value = listTypePop(o,where);
|
||||
|
||||
if (value) {
|
||||
/* Protect receiver->bpop.target, that will be
|
||||
* freed by the next unblockClient()
|
||||
* call. */
|
||||
if (dstkey) incrRefCount(dstkey);
|
||||
fastlock_lock(&receiver->lock);
|
||||
unblockClient(receiver);
|
||||
|
||||
if (serveClientBlockedOnList(receiver,
|
||||
rl->key,dstkey,rl->db,value,
|
||||
where) == C_ERR)
|
||||
{
|
||||
/* If we failed serving the client we need
|
||||
* to also undo the POP operation. */
|
||||
listTypePush(o,value,where);
|
||||
}
|
||||
|
||||
if (dstkey) decrRefCount(dstkey);
|
||||
fastlock_unlock(&receiver->lock);
|
||||
decrRefCount(value);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (listTypeLength(o) == 0) {
|
||||
dbDelete(rl->db,rl->key);
|
||||
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",rl->key,rl->db->id);
|
||||
}
|
||||
/* We don't call signalModifiedKey() as it was already called
|
||||
* when an element was pushed on the list. */
|
||||
}
|
||||
|
||||
/* Serve clients blocked on sorted set key. */
|
||||
else if (o != NULL && o->type == OBJ_ZSET) {
|
||||
dictEntry *de;
|
||||
|
||||
/* We serve clients in the same order they blocked for
|
||||
* this key, from the first blocked to the last. */
|
||||
de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
int numclients = listLength(clients);
|
||||
unsigned long zcard = zsetLength(o);
|
||||
|
||||
while(numclients-- && zcard) {
|
||||
listNode *clientnode = listFirst(clients);
|
||||
client *receiver = clientnode->value;
|
||||
|
||||
if (receiver->btype != BLOCKED_ZSET) {
|
||||
/* Put at the tail, so that at the next call
|
||||
* we'll not run into it again. */
|
||||
listDelNode(clients,clientnode);
|
||||
listAddNodeTail(clients,receiver);
|
||||
continue;
|
||||
}
|
||||
|
||||
fastlock_lock(&receiver->lock);
|
||||
int where = (receiver->lastcmd &&
|
||||
receiver->lastcmd->proc == bzpopminCommand)
|
||||
? ZSET_MIN : ZSET_MAX;
|
||||
unblockClient(receiver);
|
||||
genericZpopCommand(receiver,&rl->key,1,where,1,NULL);
|
||||
zcard--;
|
||||
|
||||
/* Replicate the command. */
|
||||
robj *argv[2];
|
||||
struct redisCommand *cmd = where == ZSET_MIN ?
|
||||
server.zpopminCommand :
|
||||
server.zpopmaxCommand;
|
||||
argv[0] = createStringObject(cmd->name,strlen(cmd->name));
|
||||
argv[1] = rl->key;
|
||||
incrRefCount(rl->key);
|
||||
propagate(cmd,receiver->db->id,
|
||||
argv,2,PROPAGATE_AOF|PROPAGATE_REPL);
|
||||
fastlock_unlock(&receiver->lock);
|
||||
decrRefCount(argv[0]);
|
||||
decrRefCount(argv[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Serve clients blocked on stream key. */
|
||||
else if (o != NULL && o->type == OBJ_STREAM) {
|
||||
dictEntry *de = dictFind(rl->db->blocking_keys,rl->key);
|
||||
stream *s = ptrFromObj(o);
|
||||
|
||||
/* We need to provide the new data arrived on the stream
|
||||
* to all the clients that are waiting for an offset smaller
|
||||
* than the current top item. */
|
||||
if (de) {
|
||||
list *clients = dictGetVal(de);
|
||||
listNode *ln;
|
||||
listIter li;
|
||||
listRewind(clients,&li);
|
||||
|
||||
while((ln = listNext(&li))) {
|
||||
client *receiver = listNodeValue(ln);
|
||||
if (receiver->btype != BLOCKED_STREAM) continue;
|
||||
streamID *gt = dictFetchValue(receiver->bpop.keys,
|
||||
rl->key);
|
||||
|
||||
/* If we blocked in the context of a consumer
|
||||
* group, we need to resolve the group and update the
|
||||
* last ID the client is blocked for: this is needed
|
||||
* because serving other clients in the same consumer
|
||||
* group will alter the "last ID" of the consumer
|
||||
* group, and clients blocked in a consumer group are
|
||||
* always blocked for the ">" ID: we need to deliver
|
||||
* only new messages and avoid unblocking the client
|
||||
* otherwise. */
|
||||
streamCG *group = NULL;
|
||||
if (receiver->bpop.xread_group) {
|
||||
group = streamLookupCG(s,
|
||||
ptrFromObj(receiver->bpop.xread_group));
|
||||
/* If the group was not found, send an error
|
||||
* to the consumer. */
|
||||
if (!group) {
|
||||
fastlock_lock(&receiver->lock);
|
||||
addReplyErrorAsync(receiver,
|
||||
"-NOGROUP the consumer group this client "
|
||||
"was blocked on no longer exists");
|
||||
unblockClient(receiver);
|
||||
fastlock_unlock(&receiver->lock);
|
||||
continue;
|
||||
} else {
|
||||
*gt = group->last_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (streamCompareID(&s->last_id, gt) > 0) {
|
||||
streamID start = *gt;
|
||||
start.seq++; /* Can't overflow, it's an uint64_t */
|
||||
|
||||
/* Lookup the consumer for the group, if any. */
|
||||
streamConsumer *consumer = NULL;
|
||||
int noack = 0;
|
||||
|
||||
if (group) {
|
||||
consumer = streamLookupConsumer(group,
|
||||
ptrFromObj(receiver->bpop.xread_consumer),
|
||||
1);
|
||||
noack = receiver->bpop.xread_group_noack;
|
||||
}
|
||||
|
||||
fastlock_lock(&receiver->lock);
|
||||
|
||||
/* Emit the two elements sub-array consisting of
|
||||
* the name of the stream and the data we
|
||||
* extracted from it. Wrapped in a single-item
|
||||
* array, since we have just one key. */
|
||||
if (receiver->resp == 2) {
|
||||
addReplyArrayLenAsync(receiver,1);
|
||||
addReplyArrayLenAsync(receiver,2);
|
||||
} else {
|
||||
addReplyMapLenAsync(receiver,1);
|
||||
}
|
||||
addReplyBulkAsync(receiver,rl->key);
|
||||
|
||||
streamPropInfo pi = {
|
||||
rl->key,
|
||||
receiver->bpop.xread_group
|
||||
};
|
||||
streamReplyWithRange(receiver,s,&start,NULL,
|
||||
receiver->bpop.xread_count,
|
||||
0, group, consumer, noack, &pi);
|
||||
|
||||
/* Note that after we unblock the client, 'gt'
|
||||
* and other receiver->bpop stuff are no longer
|
||||
* valid, so we must do the setup above before
|
||||
* this call. */
|
||||
unblockClient(receiver);
|
||||
fastlock_unlock(&receiver->lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
serverTL->fixed_time_expire--;
|
||||
|
||||
/* Free this item. */
|
||||
decrRefCount(rl->key);
|
||||
@@ -586,15 +542,17 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
|
||||
if (target != NULL) incrRefCount(target);
|
||||
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
/* Allocate our bkinfo structure, associated to each key the client
|
||||
* is blocked for. */
|
||||
bkinfo *bki = (bkinfo*)zmalloc(sizeof(*bki));
|
||||
if (btype == BLOCKED_STREAM)
|
||||
bki->stream_id = ids[j];
|
||||
/* The value associated with the key name in the bpop.keys dictionary
|
||||
* is NULL for lists and sorted sets, or the stream ID for streams. */
|
||||
void *key_data = NULL;
|
||||
if (btype == BLOCKED_STREAM) {
|
||||
key_data = zmalloc(sizeof(streamID), MALLOC_SHARED);
|
||||
memcpy(key_data,ids+j,sizeof(streamID));
|
||||
}
|
||||
|
||||
/* If the key already exists in the dictionary ignore it. */
|
||||
if (dictAdd(c->bpop.keys,keys[j],bki) != DICT_OK) {
|
||||
zfree(bki);
|
||||
if (dictAdd(c->bpop.keys,keys[j],key_data) != DICT_OK) {
|
||||
zfree(key_data);
|
||||
continue;
|
||||
}
|
||||
incrRefCount(keys[j]);
|
||||
@@ -610,10 +568,9 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
|
||||
incrRefCount(keys[j]);
|
||||
serverAssertWithInfo(c,keys[j],retval == DICT_OK);
|
||||
} else {
|
||||
l = (list*)dictGetVal(de);
|
||||
l = dictGetVal(de);
|
||||
}
|
||||
listAddNodeTail(l,c);
|
||||
bki->listnode = listLast(l);
|
||||
}
|
||||
blockClient(c,btype);
|
||||
}
|
||||
@@ -629,13 +586,12 @@ void unblockClientWaitingData(client *c) {
|
||||
di = dictGetIterator(c->bpop.keys);
|
||||
/* The client may wait for multiple keys, so unblock it for every key. */
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *key = (robj*)dictGetKey(de);
|
||||
bkinfo *bki = (bkinfo*)dictGetVal(de);
|
||||
robj *key = dictGetKey(de);
|
||||
|
||||
/* Remove this client from the list of clients waiting for this key. */
|
||||
l = (list*)dictFetchValue(c->db->blocking_keys,key);
|
||||
l = dictFetchValue(c->db->blocking_keys,key);
|
||||
serverAssertWithInfo(c,key,l != NULL);
|
||||
listDelNode(l,bki->listnode);
|
||||
listDelNode(l,listSearchKey(l,c));
|
||||
/* If the list is empty we need to remove it to avoid wasting memory */
|
||||
if (listLength(l) == 0)
|
||||
dictDelete(c->db->blocking_keys,key);
|
||||
@@ -657,12 +613,12 @@ void unblockClientWaitingData(client *c) {
|
||||
}
|
||||
|
||||
/* If the specified key has clients blocked waiting for list pushes, this
|
||||
* function will put the key reference into the g_pserver->ready_keys list.
|
||||
* function will put the key reference into the server.ready_keys list.
|
||||
* Note that db->ready_keys is a hash table that allows us to avoid putting
|
||||
* the same key again and again in the list in case of multiple pushes
|
||||
* made by a script or in the context of MULTI/EXEC.
|
||||
*
|
||||
* The list will be finally processed by handleClientsBlockedOnKeys() */
|
||||
* The list will be finally processed by handleClientsBlockedOnLists() */
|
||||
void signalKeyAsReady(redisDb *db, robj *key) {
|
||||
readyList *rl;
|
||||
|
||||
@@ -672,12 +628,12 @@ void signalKeyAsReady(redisDb *db, robj *key) {
|
||||
/* Key was already signaled? No need to queue it again. */
|
||||
if (dictFind(db->ready_keys,key) != NULL) return;
|
||||
|
||||
/* Ok, we need to queue this key into g_pserver->ready_keys. */
|
||||
rl = (readyList*)zmalloc(sizeof(*rl), MALLOC_SHARED);
|
||||
/* Ok, we need to queue this key into server.ready_keys. */
|
||||
rl = zmalloc(sizeof(*rl), MALLOC_SHARED);
|
||||
rl->key = key;
|
||||
rl->db = db;
|
||||
incrRefCount(key);
|
||||
listAddNodeTail(g_pserver->ready_keys,rl);
|
||||
listAddNodeTail(server.ready_keys,rl);
|
||||
|
||||
/* We also add the key in the db->ready_keys dictionary in order
|
||||
* to avoid adding it multiple times into a list with a simple O(1)
|
||||
@@ -34,54 +34,52 @@
|
||||
* RDB / AOF saving process from the child to the parent (for instance
|
||||
* the amount of copy on write memory used) */
|
||||
void openChildInfoPipe(void) {
|
||||
if (pipe(g_pserver->child_info_pipe) == -1) {
|
||||
if (pipe(server.child_info_pipe) == -1) {
|
||||
/* On error our two file descriptors should be still set to -1,
|
||||
* but we call anyway cloesChildInfoPipe() since can't hurt. */
|
||||
closeChildInfoPipe();
|
||||
} else if (anetNonBlock(NULL,g_pserver->child_info_pipe[0]) != ANET_OK) {
|
||||
} else if (anetNonBlock(NULL,server.child_info_pipe[0]) != ANET_OK) {
|
||||
closeChildInfoPipe();
|
||||
} else {
|
||||
memset(&g_pserver->child_info_data,0,sizeof(g_pserver->child_info_data));
|
||||
memset(&server.child_info_data,0,sizeof(server.child_info_data));
|
||||
}
|
||||
}
|
||||
|
||||
/* Close the pipes opened with openChildInfoPipe(). */
|
||||
void closeChildInfoPipe(void) {
|
||||
if (g_pserver->child_info_pipe[0] != -1 ||
|
||||
g_pserver->child_info_pipe[1] != -1)
|
||||
if (server.child_info_pipe[0] != -1 ||
|
||||
server.child_info_pipe[1] != -1)
|
||||
{
|
||||
close(g_pserver->child_info_pipe[0]);
|
||||
close(g_pserver->child_info_pipe[1]);
|
||||
g_pserver->child_info_pipe[0] = -1;
|
||||
g_pserver->child_info_pipe[1] = -1;
|
||||
close(server.child_info_pipe[0]);
|
||||
close(server.child_info_pipe[1]);
|
||||
server.child_info_pipe[0] = -1;
|
||||
server.child_info_pipe[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Send COW data to parent. The child should call this function after populating
|
||||
* the corresponding fields it want to sent (according to the process type). */
|
||||
void sendChildInfo(int ptype) {
|
||||
if (g_pserver->child_info_pipe[1] == -1) return;
|
||||
g_pserver->child_info_data.magic = CHILD_INFO_MAGIC;
|
||||
g_pserver->child_info_data.process_type = ptype;
|
||||
ssize_t wlen = sizeof(g_pserver->child_info_data);
|
||||
if (write(g_pserver->child_info_pipe[1],&g_pserver->child_info_data,wlen) != wlen) {
|
||||
if (server.child_info_pipe[1] == -1) return;
|
||||
server.child_info_data.magic = CHILD_INFO_MAGIC;
|
||||
server.child_info_data.process_type = ptype;
|
||||
ssize_t wlen = sizeof(server.child_info_data);
|
||||
if (write(server.child_info_pipe[1],&server.child_info_data,wlen) != wlen) {
|
||||
/* Nothing to do on error, this will be detected by the other side. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Receive COW data from parent. */
|
||||
void receiveChildInfo(void) {
|
||||
if (g_pserver->child_info_pipe[0] == -1) return;
|
||||
ssize_t wlen = sizeof(g_pserver->child_info_data);
|
||||
if (read(g_pserver->child_info_pipe[0],&g_pserver->child_info_data,wlen) == wlen &&
|
||||
g_pserver->child_info_data.magic == CHILD_INFO_MAGIC)
|
||||
if (server.child_info_pipe[0] == -1) return;
|
||||
ssize_t wlen = sizeof(server.child_info_data);
|
||||
if (read(server.child_info_pipe[0],&server.child_info_data,wlen) == wlen &&
|
||||
server.child_info_data.magic == CHILD_INFO_MAGIC)
|
||||
{
|
||||
if (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_RDB) {
|
||||
g_pserver->stat_rdb_cow_bytes = g_pserver->child_info_data.cow_size;
|
||||
} else if (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_AOF) {
|
||||
g_pserver->stat_aof_cow_bytes = g_pserver->child_info_data.cow_size;
|
||||
} else if (g_pserver->child_info_data.process_type == CHILD_INFO_TYPE_MODULE) {
|
||||
g_pserver->stat_module_cow_bytes = g_pserver->child_info_data.cow_size;
|
||||
if (server.child_info_data.process_type == CHILD_INFO_TYPE_RDB) {
|
||||
server.stat_rdb_cow_bytes = server.child_info_data.cow_size;
|
||||
} else if (server.child_info_data.process_type == CHILD_INFO_TYPE_AOF) {
|
||||
server.stat_aof_cow_bytes = server.child_info_data.cow_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
+631
-789
File diff suppressed because it is too large.
Load diff
+6
-3
@@ -17,10 +17,15 @@ extern "C" {
|
||||
|
||||
/* The following defines are amount of time, sometimes expressed as
|
||||
* multiplicators of the node timeout value (when ending with MULT). */
|
||||
#define CLUSTER_DEFAULT_NODE_TIMEOUT 15000
|
||||
#define CLUSTER_DEFAULT_SLAVE_VALIDITY 10 /* Slave max data age factor. */
|
||||
#define CLUSTER_DEFAULT_REQUIRE_FULL_COVERAGE 1
|
||||
#define CLUSTER_DEFAULT_SLAVE_NO_FAILOVER 0 /* Failover by default. */
|
||||
#define CLUSTER_FAIL_REPORT_VALIDITY_MULT 2 /* Fail report validity. */
|
||||
#define CLUSTER_FAIL_UNDO_TIME_MULT 2 /* Undo fail if master is back. */
|
||||
#define CLUSTER_FAIL_UNDO_TIME_ADD 10 /* Some additional time. */
|
||||
#define CLUSTER_FAILOVER_DELAY 5 /* Seconds */
|
||||
#define CLUSTER_DEFAULT_MIGRATION_BARRIER 1
|
||||
#define CLUSTER_MF_TIMEOUT 5000 /* Milliseconds to do a manual failover. */
|
||||
#define CLUSTER_MF_PAUSE_MULT 2 /* Master pause manual failover mult. */
|
||||
#define CLUSTER_SLAVE_MIGRATION_DELAY 5000 /* Delay for slave migration. */
|
||||
@@ -33,14 +38,13 @@ extern "C" {
|
||||
#define CLUSTER_REDIR_MOVED 4 /* -MOVED redirection required. */
|
||||
#define CLUSTER_REDIR_DOWN_STATE 5 /* -CLUSTERDOWN, global state. */
|
||||
#define CLUSTER_REDIR_DOWN_UNBOUND 6 /* -CLUSTERDOWN, unbound slot. */
|
||||
#define CLUSTER_REDIR_DOWN_RO_STATE 7 /* -CLUSTERDOWN, allow reads. */
|
||||
|
||||
struct clusterNode;
|
||||
|
||||
/* clusterLink encapsulates everything needed to talk with a remote node. */
|
||||
typedef struct clusterLink {
|
||||
mstime_t ctime; /* Link creation time */
|
||||
connection *conn; /* Connection to remote node */
|
||||
int fd; /* TCP socket file descriptor */
|
||||
sds sndbuf; /* Packet send buffer */
|
||||
sds rcvbuf; /* Packet reception buffer */
|
||||
struct clusterNode *node; /* Node related to this link if any, or NULL */
|
||||
@@ -128,7 +132,6 @@ typedef struct clusterNode {
|
||||
tables. */
|
||||
mstime_t ping_sent; /* Unix time we sent latest ping */
|
||||
mstime_t pong_received; /* Unix time we received the pong */
|
||||
mstime_t data_received; /* Unix time we received any data */
|
||||
mstime_t fail_time; /* Unix time when FAIL flag was set */
|
||||
mstime_t voted_time; /* Last time we voted for a slave of this master */
|
||||
mstime_t repl_offset_time; /* Unix time we received offset for this node */
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <assert.h>
|
||||
|
||||
/*************************************************
|
||||
* compactvector - similar to std::vector but optimized for minimal memory
|
||||
*
|
||||
* Notable differences:
|
||||
* - Limited to 2^32 elements
|
||||
* - Grows linearly not exponentially
|
||||
*
|
||||
*************************************************/
|
||||
|
||||
template<typename T, bool MEMMOVE_SAFE=false>
|
||||
class compactvector
|
||||
{
|
||||
static_assert(MEMMOVE_SAFE || std::is_trivially_copyable<T>::value, "compactvector requires trivially copyable types");
|
||||
T *m_data = nullptr;
|
||||
unsigned m_celem = 0;
|
||||
unsigned m_max = 0;
|
||||
|
||||
public:
|
||||
typedef T* iterator;
|
||||
|
||||
compactvector() noexcept = default;
|
||||
~compactvector() noexcept
|
||||
{
|
||||
clear(); // call dtors
|
||||
zfree(m_data);
|
||||
}
|
||||
|
||||
compactvector(compactvector &) noexcept = delete;
|
||||
|
||||
compactvector(compactvector &&src) noexcept
|
||||
{
|
||||
m_data = src.m_data;
|
||||
m_celem = src.m_celem;
|
||||
m_max = src.m_max;
|
||||
src.m_data = nullptr;
|
||||
src.m_celem = 0;
|
||||
src.m_max = 0;
|
||||
}
|
||||
|
||||
compactvector &operator=(const compactvector&) noexcept = delete;
|
||||
compactvector &operator=(compactvector &&src) noexcept
|
||||
{
|
||||
zfree(m_data);
|
||||
m_data = src.m_data;
|
||||
m_celem = src.m_celem;
|
||||
m_max = src.m_max;
|
||||
src.m_data = nullptr;
|
||||
src.m_celem = 0;
|
||||
src.m_max = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline T* begin() { return m_data; }
|
||||
inline const T* begin() const { return m_data; }
|
||||
|
||||
inline T* end() { return m_data + m_celem; }
|
||||
inline const T* end() const { return m_data + m_celem; }
|
||||
|
||||
T* insert(T* where, T &val)
|
||||
{
|
||||
assert(where >= m_data);
|
||||
size_t idx = where - m_data;
|
||||
if (m_celem >= m_max)
|
||||
{
|
||||
if (m_max < 2)
|
||||
m_max = 2;
|
||||
else
|
||||
m_max = m_max + 4;
|
||||
|
||||
m_data = (T*)zrealloc(m_data, sizeof(T) * m_max, MALLOC_LOCAL);
|
||||
m_max = zmalloc_usable(m_data) / sizeof(T);
|
||||
}
|
||||
assert(idx < m_max);
|
||||
where = m_data + idx;
|
||||
memmove(reinterpret_cast<void*>(m_data + idx + 1), reinterpret_cast<const void*>(m_data + idx), (m_celem - idx)*sizeof(T));
|
||||
new(m_data + idx) T(std::move(val));
|
||||
++m_celem;
|
||||
return where;
|
||||
}
|
||||
|
||||
T &operator[](size_t idx)
|
||||
{
|
||||
assert(idx < m_celem);
|
||||
return m_data[idx];
|
||||
}
|
||||
const T &operator[](size_t idx) const
|
||||
{
|
||||
assert(idx < m_celem);
|
||||
return m_data[idx];
|
||||
}
|
||||
|
||||
T& back() { assert(m_celem > 0); return m_data[m_celem-1]; }
|
||||
const T& back() const { assert(m_celem > 0); return m_data[m_celem-1]; }
|
||||
|
||||
void erase(T* where)
|
||||
{
|
||||
assert(where >= m_data);
|
||||
size_t idx = where - m_data;
|
||||
assert(idx < m_celem);
|
||||
where->~T();
|
||||
memmove(reinterpret_cast<void*>(where), reinterpret_cast<const void*>(where+1), ((m_celem - idx - 1)*sizeof(T)));
|
||||
--m_celem;
|
||||
|
||||
if (m_celem == 0)
|
||||
{
|
||||
zfree(m_data);
|
||||
m_data = nullptr;
|
||||
m_max = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void shrink_to_fit()
|
||||
{
|
||||
if (m_max == m_celem)
|
||||
return;
|
||||
m_data = (T*)zrealloc(m_data, sizeof(T) * m_celem, MALLOC_LOCAL);
|
||||
m_max = m_celem; // NOTE: We do not get the usable size here, because this could cause us to continually realloc
|
||||
}
|
||||
|
||||
size_t bytes_used() const
|
||||
{
|
||||
return sizeof(this) + (m_max * sizeof(T));
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
for (size_t idx = 0; idx < m_celem; ++idx)
|
||||
m_data[idx].~T();
|
||||
zfree(m_data);
|
||||
m_data = nullptr;
|
||||
m_celem = 0;
|
||||
m_max = 0;
|
||||
}
|
||||
|
||||
bool empty() const noexcept
|
||||
{
|
||||
return m_celem == 0;
|
||||
}
|
||||
|
||||
size_t size() const noexcept
|
||||
{
|
||||
return m_celem;
|
||||
}
|
||||
|
||||
T* data() noexcept { return m_data; }
|
||||
const T* data() const noexcept { return m_data; }
|
||||
};
|
||||
static_assert(sizeof(compactvector<void*>) <= 16, "not compact");
|
||||
+2427
File diff suppressed because it is too large.
Load diff
-2379
File diff suppressed because it is too large.
Load diff
@@ -232,37 +232,4 @@ void setproctitle(const char *fmt, ...);
|
||||
#define USE_ALIGNED_ACCESS
|
||||
#endif
|
||||
|
||||
/* Define for redis_set_thread_title */
|
||||
#ifdef __linux__
|
||||
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name)
|
||||
#else
|
||||
#if (defined __FreeBSD__ || defined __OpenBSD__)
|
||||
#include <pthread_np.h>
|
||||
#define redis_set_thread_title(name) pthread_set_name_np(pthread_self(), name)
|
||||
#elif defined __NetBSD__
|
||||
#include <pthread.h>
|
||||
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name, NULL)
|
||||
#else
|
||||
#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7))
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
int pthread_setname_np(const char *name);
|
||||
#include <pthread.h>
|
||||
#define redis_set_thread_title(name) pthread_setname_np(name)
|
||||
#else
|
||||
#define redis_set_thread_title(name)
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Check if we can use setcpuaffinity(). */
|
||||
#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__)
|
||||
#define USE_SETCPUAFFINITY
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
void setcpuaffinity(const char *cpulist);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,431 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include "connhelpers.h"
|
||||
|
||||
/* The connections module provides a lean abstraction of network connections
|
||||
* to avoid direct socket and async event management across the Redis code base.
|
||||
*
|
||||
* It does NOT provide advanced connection features commonly found in similar
|
||||
* libraries such as complete in/out buffer management, throttling, etc. These
|
||||
* functions remain in networking.c.
|
||||
*
|
||||
* The primary goal is to allow transparent handling of TCP and TLS based
|
||||
* connections. To do so, connections have the following properties:
|
||||
*
|
||||
* 1. A connection may live before its corresponding socket exists. This
|
||||
* allows various context and configuration setting to be handled before
|
||||
* establishing the actual connection.
|
||||
* 2. The caller may register/unregister logical read/write handlers to be
|
||||
* called when the connection has data to read from/can accept writes.
|
||||
* These logical handlers may or may not correspond to actual AE events,
|
||||
* depending on the implementation (for TCP they are; for TLS they aren't).
|
||||
*/
|
||||
|
||||
extern ConnectionType CT_Socket;
|
||||
|
||||
/* When a connection is created we must know its type already, but the
|
||||
* underlying socket may or may not exist:
|
||||
*
|
||||
* - For accepted connections, it exists as we do not model the listen/accept
|
||||
* part; So caller calls connCreateSocket() followed by connAccept().
|
||||
* - For outgoing connections, the socket is created by the connection module
|
||||
* itself; So caller calls connCreateSocket() followed by connConnect(),
|
||||
* which registers a connect callback that fires on connected/error state
|
||||
* (and after any transport level handshake was done).
|
||||
*
|
||||
* NOTE: An earlier version relied on connections being part of other structs
|
||||
* and not independently allocated. This could lead to further optimizations
|
||||
* like using container_of(), etc. However it was discontinued in favor of
|
||||
* this approach for these reasons:
|
||||
*
|
||||
* 1. In some cases conns are created/handled outside the context of the
|
||||
* containing struct, in which case it gets a bit awkward to copy them.
|
||||
* 2. Future implementations may wish to allocate arbitrary data for the
|
||||
* connection.
|
||||
* 3. The container_of() approach is anyway risky because connections may
|
||||
* be embedded in different structs, not just client.
|
||||
*/
|
||||
|
||||
connection *connCreateSocket() {
|
||||
connection *conn = (connection*)zcalloc(sizeof(connection), MALLOC_LOCAL);
|
||||
conn->type = &CT_Socket;
|
||||
conn->fd = -1;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/* Create a new socket-type connection that is already associated with
|
||||
* an accepted connection.
|
||||
*
|
||||
* The socket is not read for I/O until connAccept() was called and
|
||||
* invoked the connection-level accept handler.
|
||||
*/
|
||||
connection *connCreateAcceptedSocket(int fd) {
|
||||
connection *conn = connCreateSocket();
|
||||
conn->fd = fd;
|
||||
conn->state = CONN_STATE_ACCEPTING;
|
||||
return conn;
|
||||
}
|
||||
|
||||
static int connSocketConnect(connection *conn, const char *addr, int port, const char *src_addr,
|
||||
ConnectionCallbackFunc connect_handler) {
|
||||
int fd = anetTcpNonBlockBestEffortBindConnect(NULL,addr,port,src_addr);
|
||||
if (fd == -1) {
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
conn->last_errno = errno;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
conn->fd = fd;
|
||||
conn->state = CONN_STATE_CONNECTING;
|
||||
|
||||
conn->conn_handler = connect_handler;
|
||||
aeCreateFileEvent(serverTL->el, conn->fd, AE_WRITABLE,
|
||||
conn->type->ae_handler, conn);
|
||||
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Returns true if a write handler is registered */
|
||||
int connHasWriteHandler(connection *conn) {
|
||||
return conn->write_handler != NULL;
|
||||
}
|
||||
|
||||
/* Returns true if a read handler is registered */
|
||||
int connHasReadHandler(connection *conn) {
|
||||
return conn->read_handler != NULL;
|
||||
}
|
||||
|
||||
/* Associate a private data pointer with the connection */
|
||||
void connSetPrivateData(connection *conn, void *data) {
|
||||
conn->private_data = data;
|
||||
}
|
||||
|
||||
/* Get the associated private data pointer */
|
||||
void *connGetPrivateData(connection *conn) {
|
||||
return conn->private_data;
|
||||
}
|
||||
|
||||
/* ------ Pure socket connections ------- */
|
||||
|
||||
/* A very incomplete list of implementation-specific calls. Much of the above shall
|
||||
* move here as we implement additional connection types.
|
||||
*/
|
||||
|
||||
/* Close the connection and free resources. */
|
||||
static void connSocketClose(connection *conn) {
|
||||
if (conn->fd != -1) {
|
||||
aeDeleteFileEvent(serverTL->el,conn->fd,AE_READABLE);
|
||||
aeDeleteFileEvent(serverTL->el,conn->fd,AE_WRITABLE);
|
||||
close(conn->fd);
|
||||
conn->fd = -1;
|
||||
}
|
||||
|
||||
/* If called from within a handler, schedule the close but
|
||||
* keep the connection until the handler returns.
|
||||
*/
|
||||
if (connHasRefs(conn)) {
|
||||
conn->flags |= CONN_FLAG_CLOSE_SCHEDULED;
|
||||
return;
|
||||
}
|
||||
|
||||
zfree(conn);
|
||||
}
|
||||
|
||||
static int connSocketWrite(connection *conn, const void *data, size_t data_len) {
|
||||
int ret = write(conn->fd, data, data_len);
|
||||
if (ret < 0 && errno != EAGAIN) {
|
||||
conn->last_errno = errno;
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int connSocketRead(connection *conn, void *buf, size_t buf_len) {
|
||||
int ret = read(conn->fd, buf, buf_len);
|
||||
if (!ret) {
|
||||
conn->state = CONN_STATE_CLOSED;
|
||||
} else if (ret < 0 && errno != EAGAIN) {
|
||||
conn->last_errno = errno;
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int connSocketAccept(connection *conn, ConnectionCallbackFunc accept_handler) {
|
||||
int ret = C_OK;
|
||||
|
||||
if (conn->state != CONN_STATE_ACCEPTING) return C_ERR;
|
||||
conn->state = CONN_STATE_CONNECTED;
|
||||
|
||||
connIncrRefs(conn);
|
||||
if (!callHandler(conn, accept_handler)) ret = C_ERR;
|
||||
connDecrRefs(conn);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register a write handler, to be called when the connection is writable.
|
||||
* If NULL, the existing handler is removed.
|
||||
*
|
||||
* The barrier flag indicates a write barrier is requested, resulting with
|
||||
* CONN_FLAG_WRITE_BARRIER set. This will ensure that the write handler is
|
||||
* always called before and not after the read handler in a single event
|
||||
* loop.
|
||||
*/
|
||||
static int connSocketSetWriteHandler(connection *conn, ConnectionCallbackFunc func, int barrier, bool fThreadSafe) {
|
||||
if (func == conn->write_handler) return C_OK;
|
||||
|
||||
conn->write_handler = func;
|
||||
if (barrier)
|
||||
conn->flags |= CONN_FLAG_WRITE_BARRIER;
|
||||
else
|
||||
conn->flags &= ~CONN_FLAG_WRITE_BARRIER;
|
||||
|
||||
int flags = AE_WRITABLE;
|
||||
if (fThreadSafe)
|
||||
flags |= AE_WRITE_THREADSAFE;
|
||||
|
||||
if (!conn->write_handler)
|
||||
aeDeleteFileEvent(serverTL->el,conn->fd,AE_WRITABLE);
|
||||
else
|
||||
if (aeCreateFileEvent(serverTL->el,conn->fd,flags,
|
||||
conn->type->ae_handler,conn) == AE_ERR) return C_ERR;
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Register a read handler, to be called when the connection is readable.
|
||||
* If NULL, the existing handler is removed.
|
||||
*/
|
||||
static int connSocketSetReadHandler(connection *conn, ConnectionCallbackFunc func, bool fThreadSafe) {
|
||||
if (func == conn->read_handler) return C_OK;
|
||||
|
||||
int flags = AE_READABLE;
|
||||
if (fThreadSafe)
|
||||
flags |= AE_READ_THREADSAFE;
|
||||
|
||||
conn->read_handler = func;
|
||||
if (!conn->read_handler)
|
||||
aeDeleteFileEvent(serverTL->el,conn->fd,AE_READABLE);
|
||||
else
|
||||
if (aeCreateFileEvent(serverTL->el,conn->fd,
|
||||
flags,conn->type->ae_handler,conn) == AE_ERR) return C_ERR;
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
static const char *connSocketGetLastError(connection *conn) {
|
||||
return strerror(conn->last_errno);
|
||||
}
|
||||
|
||||
static void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask)
|
||||
{
|
||||
UNUSED(el);
|
||||
UNUSED(fd);
|
||||
connection *conn = (connection*)clientData;
|
||||
|
||||
if (conn->state == CONN_STATE_CONNECTING &&
|
||||
(mask & AE_WRITABLE) && conn->conn_handler) {
|
||||
|
||||
if (connGetSocketError(conn)) {
|
||||
conn->last_errno = errno;
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
} else {
|
||||
conn->state = CONN_STATE_CONNECTED;
|
||||
}
|
||||
|
||||
if (!conn->write_handler) aeDeleteFileEvent(serverTL->el,conn->fd,AE_WRITABLE);
|
||||
|
||||
if (!callHandler(conn, conn->conn_handler)) return;
|
||||
conn->conn_handler = NULL;
|
||||
}
|
||||
|
||||
/* Normally we execute the readable event first, and the writable
|
||||
* event later. This is useful as sometimes we may be able
|
||||
* to serve the reply of a query immediately after processing the
|
||||
* query.
|
||||
*
|
||||
* However if WRITE_BARRIER is set in the mask, our application is
|
||||
* asking us to do the reverse: never fire the writable event
|
||||
* after the readable. In such a case, we invert the calls.
|
||||
* This is useful when, for instance, we want to do things
|
||||
* in the beforeSleep() hook, like fsync'ing a file to disk,
|
||||
* before replying to a client. */
|
||||
int invert = conn->flags & CONN_FLAG_WRITE_BARRIER;
|
||||
|
||||
int call_write = (mask & AE_WRITABLE) && conn->write_handler;
|
||||
int call_read = (mask & AE_READABLE) && conn->read_handler;
|
||||
|
||||
/* Handle normal I/O flows */
|
||||
if (!invert && call_read) {
|
||||
if (!callHandler(conn, conn->read_handler)) return;
|
||||
}
|
||||
/* Fire the writable event. */
|
||||
if (call_write) {
|
||||
if (!callHandler(conn, conn->write_handler)) return;
|
||||
}
|
||||
/* If we have to invert the call, fire the readable event now
|
||||
* after the writable one. */
|
||||
if (invert && call_read) {
|
||||
if (!callHandler(conn, conn->read_handler)) return;
|
||||
}
|
||||
}
|
||||
|
||||
static int connSocketBlockingConnect(connection *conn, const char *addr, int port, long long timeout) {
|
||||
int fd = anetTcpNonBlockConnect(NULL,addr,port);
|
||||
if (fd == -1) {
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
conn->last_errno = errno;
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
if ((aeWait(fd, AE_WRITABLE, timeout) & AE_WRITABLE) == 0) {
|
||||
conn->state = CONN_STATE_ERROR;
|
||||
conn->last_errno = ETIMEDOUT;
|
||||
}
|
||||
|
||||
conn->fd = fd;
|
||||
conn->state = CONN_STATE_CONNECTED;
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Connection-based versions of syncio.c functions.
|
||||
* NOTE: This should ideally be refactored out in favor of pure async work.
|
||||
*/
|
||||
|
||||
static ssize_t connSocketSyncWrite(connection *conn, const char *ptr, ssize_t size, long long timeout) {
|
||||
return syncWrite(conn->fd, ptr, size, timeout);
|
||||
}
|
||||
|
||||
static ssize_t connSocketSyncRead(connection *conn, char *ptr, ssize_t size, long long timeout) {
|
||||
return syncRead(conn->fd, ptr, size, timeout);
|
||||
}
|
||||
|
||||
static ssize_t connSocketSyncReadLine(connection *conn, char *ptr, ssize_t size, long long timeout) {
|
||||
return syncReadLine(conn->fd, ptr, size, timeout);
|
||||
}
|
||||
|
||||
|
||||
ConnectionType CT_Socket = {
|
||||
connSocketEventHandler,
|
||||
connSocketConnect,
|
||||
connSocketWrite,
|
||||
connSocketRead,
|
||||
connSocketClose,
|
||||
connSocketAccept,
|
||||
connSocketSetWriteHandler,
|
||||
connSocketSetReadHandler,
|
||||
connSocketGetLastError,
|
||||
connSocketBlockingConnect,
|
||||
connSocketSyncWrite,
|
||||
connSocketSyncRead,
|
||||
connSocketSyncReadLine
|
||||
};
|
||||
|
||||
|
||||
int connGetSocketError(connection *conn) {
|
||||
int sockerr = 0;
|
||||
socklen_t errlen = sizeof(sockerr);
|
||||
|
||||
if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &sockerr, &errlen) == -1)
|
||||
sockerr = errno;
|
||||
return sockerr;
|
||||
}
|
||||
|
||||
int connPeerToString(connection *conn, char *ip, size_t ip_len, int *port) {
|
||||
return anetPeerToString(conn ? conn->fd : -1, ip, ip_len, port);
|
||||
}
|
||||
|
||||
int connFormatPeer(connection *conn, char *buf, size_t buf_len) {
|
||||
return anetFormatPeer(conn ? conn->fd : -1, buf, buf_len);
|
||||
}
|
||||
|
||||
int connSockName(connection *conn, char *ip, size_t ip_len, int *port) {
|
||||
return anetSockName(conn->fd, ip, ip_len, port);
|
||||
}
|
||||
|
||||
int connBlock(connection *conn) {
|
||||
if (conn->fd == -1) return C_ERR;
|
||||
return anetBlock(NULL, conn->fd);
|
||||
}
|
||||
|
||||
int connNonBlock(connection *conn) {
|
||||
if (conn->fd == -1) return C_ERR;
|
||||
return anetNonBlock(NULL, conn->fd);
|
||||
}
|
||||
|
||||
int connEnableTcpNoDelay(connection *conn) {
|
||||
if (conn->fd == -1) return C_ERR;
|
||||
return anetEnableTcpNoDelay(NULL, conn->fd);
|
||||
}
|
||||
|
||||
int connDisableTcpNoDelay(connection *conn) {
|
||||
if (conn->fd == -1) return C_ERR;
|
||||
return anetDisableTcpNoDelay(NULL, conn->fd);
|
||||
}
|
||||
|
||||
int connKeepAlive(connection *conn, int interval) {
|
||||
if (conn->fd == -1) return C_ERR;
|
||||
return anetKeepAlive(NULL, conn->fd, interval);
|
||||
}
|
||||
|
||||
int connSendTimeout(connection *conn, long long ms) {
|
||||
return anetSendTimeout(NULL, conn->fd, ms);
|
||||
}
|
||||
|
||||
int connRecvTimeout(connection *conn, long long ms) {
|
||||
return anetRecvTimeout(NULL, conn->fd, ms);
|
||||
}
|
||||
|
||||
int connGetState(connection *conn) {
|
||||
return conn->state;
|
||||
}
|
||||
|
||||
void connSetThreadAffinity(connection *conn, int cpu) {
|
||||
#ifdef HAVE_SO_INCOMING_CPU
|
||||
if (setsockopt(conn->fd, SOL_SOCKET, SO_INCOMING_CPU, &cpu, sizeof(cpu)) != 0)
|
||||
{
|
||||
serverLog(LL_WARNING, "Failed to set socket affinity");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return a text that describes the connection, suitable for inclusion
|
||||
* in CLIENT LIST and similar outputs.
|
||||
*
|
||||
* For sockets, we always return "fd=<fdnum>" to maintain compatibility.
|
||||
*/
|
||||
const char *connGetInfo(connection *conn, char *buf, size_t buf_len) {
|
||||
snprintf(buf, buf_len-1, "fd=%i", conn->fd);
|
||||
return buf;
|
||||
}
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __REDIS_CONNECTION_H
|
||||
#define __REDIS_CONNECTION_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#define CONN_INFO_LEN 32
|
||||
|
||||
struct aeEventLoop;
|
||||
typedef struct connection connection;
|
||||
|
||||
typedef enum {
|
||||
CONN_STATE_NONE = 0,
|
||||
CONN_STATE_CONNECTING,
|
||||
CONN_STATE_ACCEPTING,
|
||||
CONN_STATE_CONNECTED,
|
||||
CONN_STATE_CLOSED,
|
||||
CONN_STATE_ERROR
|
||||
} ConnectionState;
|
||||
|
||||
#define CONN_FLAG_CLOSE_SCHEDULED (1<<0) /* Closed scheduled by a handler */
|
||||
#define CONN_FLAG_WRITE_BARRIER (1<<1) /* Write barrier requested */
|
||||
#define CONN_FLAG_READ_THREADSAFE (1<<2)
|
||||
#define CONN_FLAG_WRITE_THREADSAFE (1<<3)
|
||||
|
||||
typedef void (*ConnectionCallbackFunc)(struct connection *conn);
|
||||
|
||||
typedef struct ConnectionType {
|
||||
void (*ae_handler)(struct aeEventLoop *el, int fd, void *clientData, int mask);
|
||||
int (*connect)(struct connection *conn, const char *addr, int port, const char *source_addr, ConnectionCallbackFunc connect_handler);
|
||||
int (*write)(struct connection *conn, const void *data, size_t data_len);
|
||||
int (*read)(struct connection *conn, void *buf, size_t buf_len);
|
||||
void (*close)(struct connection *conn);
|
||||
int (*accept)(struct connection *conn, ConnectionCallbackFunc accept_handler);
|
||||
int (*set_write_handler)(struct connection *conn, ConnectionCallbackFunc handler, int barrier, bool fThreadSafe);
|
||||
int (*set_read_handler)(struct connection *conn, ConnectionCallbackFunc handler, bool fThreadSafe);
|
||||
const char *(*get_last_error)(struct connection *conn);
|
||||
int (*blocking_connect)(struct connection *conn, const char *addr, int port, long long timeout);
|
||||
ssize_t (*sync_write)(struct connection *conn, const char *ptr, ssize_t size, long long timeout);
|
||||
ssize_t (*sync_read)(struct connection *conn, char *ptr, ssize_t size, long long timeout);
|
||||
ssize_t (*sync_readline)(struct connection *conn, char *ptr, ssize_t size, long long timeout);
|
||||
void (*marshal_thread)(struct connection *conn);
|
||||
} ConnectionType;
|
||||
|
||||
struct connection {
|
||||
ConnectionType *type;
|
||||
std::atomic<ConnectionState> state;
|
||||
short int flags;
|
||||
short int refs;
|
||||
int last_errno;
|
||||
void *private_data;
|
||||
ConnectionCallbackFunc conn_handler;
|
||||
ConnectionCallbackFunc write_handler;
|
||||
ConnectionCallbackFunc read_handler;
|
||||
int fd;
|
||||
};
|
||||
|
||||
/* The connection module does not deal with listening and accepting sockets,
|
||||
* so we assume we have a socket when an incoming connection is created.
|
||||
*
|
||||
* The fd supplied should therefore be associated with an already accept()ed
|
||||
* socket.
|
||||
*
|
||||
* connAccept() may directly call accept_handler(), or return and call it
|
||||
* at a later time. This behavior is a bit awkward but aims to reduce the need
|
||||
* to wait for the next event loop, if no additional handshake is required.
|
||||
*
|
||||
* IMPORTANT: accept_handler may decide to close the connection, calling connClose().
|
||||
* To make this safe, the connection is only marked with CONN_FLAG_CLOSE_SCHEDULED
|
||||
* in this case, and connAccept() returns with an error.
|
||||
*
|
||||
* connAccept() callers must always check the return value and on error (C_ERR)
|
||||
* a connClose() must be called.
|
||||
*/
|
||||
|
||||
static inline int connAccept(connection *conn, ConnectionCallbackFunc accept_handler) {
|
||||
return conn->type->accept(conn, accept_handler);
|
||||
}
|
||||
|
||||
/* Establish a connection. The connect_handler will be called when the connection
|
||||
* is established, or if an error has occured.
|
||||
*
|
||||
* The connection handler will be responsible to set up any read/write handlers
|
||||
* as needed.
|
||||
*
|
||||
* If C_ERR is returned, the operation failed and the connection handler shall
|
||||
* not be expected.
|
||||
*/
|
||||
static inline int connConnect(connection *conn, const char *addr, int port, const char *src_addr,
|
||||
ConnectionCallbackFunc connect_handler) {
|
||||
return conn->type->connect(conn, addr, port, src_addr, connect_handler);
|
||||
}
|
||||
|
||||
/* Blocking connect.
|
||||
*
|
||||
* NOTE: This is implemented in order to simplify the transition to the abstract
|
||||
* connections, but should probably be refactored out of cluster.c and replication.c,
|
||||
* in favor of a pure async implementation.
|
||||
*/
|
||||
static inline int connBlockingConnect(connection *conn, const char *addr, int port, long long timeout) {
|
||||
return conn->type->blocking_connect(conn, addr, port, timeout);
|
||||
}
|
||||
|
||||
/* Write to connection, behaves the same as write(2).
|
||||
*
|
||||
* Like write(2), a short write is possible. A -1 return indicates an error.
|
||||
*
|
||||
* The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use
|
||||
* connGetState() to see if the connection state is still CONN_STATE_CONNECTED.
|
||||
*/
|
||||
static inline int connWrite(connection *conn, const void *data, size_t data_len) {
|
||||
return conn->type->write(conn, data, data_len);
|
||||
}
|
||||
|
||||
/* Read from the connection, behaves the same as read(2).
|
||||
*
|
||||
* Like read(2), a short read is possible. A return value of 0 will indicate the
|
||||
* connection was closed, and -1 will indicate an error.
|
||||
*
|
||||
* The caller should NOT rely on errno. Testing for an EAGAIN-like condition, use
|
||||
* connGetState() to see if the connection state is still CONN_STATE_CONNECTED.
|
||||
*/
|
||||
static inline int connRead(connection *conn, void *buf, size_t buf_len) {
|
||||
return conn->type->read(conn, buf, buf_len);
|
||||
}
|
||||
|
||||
/* Register a write handler, to be called when the connection is writable.
|
||||
* If NULL, the existing handler is removed.
|
||||
*/
|
||||
static inline int connSetWriteHandler(connection *conn, ConnectionCallbackFunc func, bool fThreadSafe = false) {
|
||||
return conn->type->set_write_handler(conn, func, 0, fThreadSafe);
|
||||
}
|
||||
|
||||
/* Register a read handler, to be called when the connection is readable.
|
||||
* If NULL, the existing handler is removed.
|
||||
*/
|
||||
static inline int connSetReadHandler(connection *conn, ConnectionCallbackFunc func, bool fThreadSafe = false) {
|
||||
return conn->type->set_read_handler(conn, func, fThreadSafe);
|
||||
}
|
||||
|
||||
/* Set a write handler, and possibly enable a write barrier, this flag is
|
||||
* cleared when write handler is changed or removed.
|
||||
* With barroer enabled, we never fire the event if the read handler already
|
||||
* fired in the same event loop iteration. Useful when you want to persist
|
||||
* things to disk before sending replies, and want to do that in a group fashion. */
|
||||
static inline int connSetWriteHandlerWithBarrier(connection *conn, ConnectionCallbackFunc func, int barrier, bool fThreadSafe = false) {
|
||||
return conn->type->set_write_handler(conn, func, barrier, fThreadSafe);
|
||||
}
|
||||
|
||||
static inline void connClose(connection *conn) {
|
||||
conn->type->close(conn);
|
||||
}
|
||||
|
||||
/* Returns the last error encountered by the connection, as a string. If no error,
|
||||
* a NULL is returned.
|
||||
*/
|
||||
static inline const char *connGetLastError(connection *conn) {
|
||||
return conn->type->get_last_error(conn);
|
||||
}
|
||||
|
||||
static inline ssize_t connSyncWrite(connection *conn, const char *ptr, ssize_t size, long long timeout) {
|
||||
return conn->type->sync_write(conn, ptr, size, timeout);
|
||||
}
|
||||
|
||||
static inline ssize_t connSyncRead(connection *conn, char *ptr, ssize_t size, long long timeout) {
|
||||
return conn->type->sync_read(conn, ptr, size, timeout);
|
||||
}
|
||||
|
||||
static inline ssize_t connSyncReadLine(connection *conn, char *ptr, ssize_t size, long long timeout) {
|
||||
return conn->type->sync_readline(conn, ptr, size, timeout);
|
||||
}
|
||||
|
||||
static inline void connMarshalThread(connection *conn) {
|
||||
if (conn->type->marshal_thread != nullptr)
|
||||
conn->type->marshal_thread(conn);
|
||||
}
|
||||
|
||||
connection *connCreateSocket();
|
||||
connection *connCreateAcceptedSocket(int fd);
|
||||
|
||||
connection *connCreateTLS();
|
||||
connection *connCreateAcceptedTLS(int fd, int require_auth);
|
||||
|
||||
void connSetPrivateData(connection *conn, void *data);
|
||||
void *connGetPrivateData(connection *conn);
|
||||
int connGetState(connection *conn);
|
||||
int connHasWriteHandler(connection *conn);
|
||||
int connHasReadHandler(connection *conn);
|
||||
int connGetSocketError(connection *conn);
|
||||
void connSetThreadAffinity(connection *conn, int cpu);
|
||||
|
||||
/* anet-style wrappers to conns */
|
||||
int connBlock(connection *conn);
|
||||
int connNonBlock(connection *conn);
|
||||
int connEnableTcpNoDelay(connection *conn);
|
||||
int connDisableTcpNoDelay(connection *conn);
|
||||
int connKeepAlive(connection *conn, int interval);
|
||||
int connSendTimeout(connection *conn, long long ms);
|
||||
int connRecvTimeout(connection *conn, long long ms);
|
||||
int connPeerToString(connection *conn, char *ip, size_t ip_len, int *port);
|
||||
int connFormatPeer(connection *conn, char *buf, size_t buf_len);
|
||||
int connSockName(connection *conn, char *ip, size_t ip_len, int *port);
|
||||
const char *connGetInfo(connection *conn, char *buf, size_t buf_len);
|
||||
|
||||
/* Helpers for tls special considerations */
|
||||
int tlsHasPendingData();
|
||||
int tlsProcessPendingData();
|
||||
|
||||
#endif /* __REDIS_CONNECTION_H */
|
||||
@@ -1,88 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019, Redis Labs
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __REDIS_CONNHELPERS_H
|
||||
#define __REDIS_CONNHELPERS_H
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
/* These are helper functions that are common to different connection
|
||||
* implementations (currently sockets in connection.c and TLS in tls.c).
|
||||
*
|
||||
* Currently helpers implement the mechanisms for invoking connection
|
||||
* handlers and tracking connection references, to allow safe destruction
|
||||
* of connections from within a handler.
|
||||
*/
|
||||
|
||||
/* Incremenet connection references.
|
||||
*
|
||||
* Inside a connection handler, we guarantee refs >= 1 so it is always
|
||||
* safe to connClose().
|
||||
*
|
||||
* In other cases where we don't want to prematurely lose the connection,
|
||||
* it can go beyond 1 as well; currently it is only done by connAccept().
|
||||
*/
|
||||
static inline void connIncrRefs(connection *conn) {
|
||||
conn->refs++;
|
||||
}
|
||||
|
||||
/* Decrement connection references.
|
||||
*
|
||||
* Note that this is not intended to provide any automatic free logic!
|
||||
* callHandler() takes care of that for the common flows, and anywhere an
|
||||
* explicit connIncrRefs() is used, the caller is expected to take care of
|
||||
* that.
|
||||
*/
|
||||
|
||||
static inline void connDecrRefs(connection *conn) {
|
||||
conn->refs--;
|
||||
}
|
||||
|
||||
static inline int connHasRefs(connection *conn) {
|
||||
return conn->refs;
|
||||
}
|
||||
|
||||
/* Helper for connection implementations to call handlers:
|
||||
* 1. Increment refs to protect the connection.
|
||||
* 2. Execute the handler (if set).
|
||||
* 3. Decrement refs and perform deferred close, if refs==0.
|
||||
*/
|
||||
static inline int callHandler(connection *conn, ConnectionCallbackFunc handler) {
|
||||
connIncrRefs(conn);
|
||||
if (handler) handler(conn);
|
||||
connDecrRefs(conn);
|
||||
if (conn->flags & CONN_FLAG_CLOSE_SCHEDULED) {
|
||||
if (!connHasRefs(conn)) connClose(conn);
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* __REDIS_CONNHELPERS_H */
|
||||
+2
-2
@@ -1,3 +1,5 @@
|
||||
#include "server.h"
|
||||
|
||||
/*
|
||||
* Copyright 2001-2010 Georges Menie (www.menie.org)
|
||||
* Copyright 2010-2012 Salvatore Sanfilippo (adapted to Redis coding style)
|
||||
@@ -27,8 +29,6 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* CRC16 implementation according to CCITT standards.
|
||||
*
|
||||
* Note by @antirez: this is actually the XMODEM CRC 16 algorithm, using the
|
||||
|
||||
+153
-118
@@ -1,5 +1,16 @@
|
||||
/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>
|
||||
* Copyright (c) 2020, Amazon Web Services
|
||||
/* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0.
|
||||
*
|
||||
* Specification of this CRC64 variant follows:
|
||||
* Name: crc-64-jones
|
||||
* Width: 64 bites
|
||||
* Poly: 0xad93d23594c935a9
|
||||
* Reflected In: True
|
||||
* Xor_In: 0xffffffffffffffff
|
||||
* Reflected_Out: True
|
||||
* Xor_Out: 0x0
|
||||
* Check("123456789"): 0xe9c6d914c4b8d9ca
|
||||
*
|
||||
* Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -26,100 +37,147 @@
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE. */
|
||||
|
||||
#include "crc64.h"
|
||||
#include "crcspeed.h"
|
||||
static uint64_t crc64_table[8][256] = {{0}};
|
||||
#include <stdint.h>
|
||||
|
||||
#define POLY UINT64_C(0xad93d23594c935a9)
|
||||
/******************** BEGIN GENERATED PYCRC FUNCTIONS ********************/
|
||||
/**
|
||||
* Generated on Sun Dec 21 14:14:07 2014,
|
||||
* by pycrc v0.8.2, https://www.tty1.net/pycrc/
|
||||
*
|
||||
* LICENSE ON GENERATED CODE:
|
||||
* ==========================
|
||||
* As of version 0.6, pycrc is released under the terms of the MIT licence.
|
||||
* The code generated by pycrc is not considered a substantial portion of the
|
||||
* software, therefore the author of pycrc will not claim any copyright on
|
||||
* the generated code.
|
||||
* ==========================
|
||||
*
|
||||
* CRC configuration:
|
||||
* Width = 64
|
||||
* Poly = 0xad93d23594c935a9
|
||||
* XorIn = 0xffffffffffffffff
|
||||
* ReflectIn = True
|
||||
* XorOut = 0x0000000000000000
|
||||
* ReflectOut = True
|
||||
* Algorithm = bit-by-bit-fast
|
||||
*
|
||||
* Modifications after generation (by matt):
|
||||
* - included finalize step in-line with update for single-call generation
|
||||
* - re-worked some inner variable architectures
|
||||
* - adjusted function parameters to match expected prototypes.
|
||||
*****************************************************************************/
|
||||
static const uint64_t crc64_tab[256] = {
|
||||
UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),
|
||||
UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),
|
||||
UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),
|
||||
UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),
|
||||
UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),
|
||||
UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),
|
||||
UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),
|
||||
UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),
|
||||
UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),
|
||||
UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),
|
||||
UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),
|
||||
UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),
|
||||
UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),
|
||||
UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),
|
||||
UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),
|
||||
UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),
|
||||
UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),
|
||||
UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),
|
||||
UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),
|
||||
UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),
|
||||
UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),
|
||||
UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),
|
||||
UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),
|
||||
UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),
|
||||
UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),
|
||||
UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),
|
||||
UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),
|
||||
UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),
|
||||
UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),
|
||||
UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),
|
||||
UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),
|
||||
UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),
|
||||
UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),
|
||||
UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),
|
||||
UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),
|
||||
UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),
|
||||
UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),
|
||||
UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),
|
||||
UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),
|
||||
UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),
|
||||
UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),
|
||||
UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),
|
||||
UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),
|
||||
UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),
|
||||
UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),
|
||||
UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),
|
||||
UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),
|
||||
UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),
|
||||
UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),
|
||||
UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),
|
||||
UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),
|
||||
UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),
|
||||
UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),
|
||||
UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),
|
||||
UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),
|
||||
UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),
|
||||
UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),
|
||||
UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),
|
||||
UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),
|
||||
UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),
|
||||
UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),
|
||||
UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),
|
||||
UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),
|
||||
UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),
|
||||
UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),
|
||||
UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),
|
||||
UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),
|
||||
UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),
|
||||
UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),
|
||||
UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),
|
||||
UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),
|
||||
UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),
|
||||
UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),
|
||||
UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),
|
||||
UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),
|
||||
UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),
|
||||
UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),
|
||||
UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),
|
||||
UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),
|
||||
UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),
|
||||
UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),
|
||||
UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),
|
||||
UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),
|
||||
UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),
|
||||
UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),
|
||||
UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),
|
||||
UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),
|
||||
UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),
|
||||
UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),
|
||||
UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),
|
||||
UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),
|
||||
UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),
|
||||
UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),
|
||||
UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),
|
||||
UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),
|
||||
UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),
|
||||
UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),
|
||||
UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),
|
||||
UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),
|
||||
UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),
|
||||
UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),
|
||||
UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),
|
||||
UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),
|
||||
UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),
|
||||
UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),
|
||||
UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),
|
||||
UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),
|
||||
UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),
|
||||
UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),
|
||||
UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),
|
||||
UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),
|
||||
UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),
|
||||
UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),
|
||||
UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),
|
||||
UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),
|
||||
UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),
|
||||
UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),
|
||||
UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),
|
||||
UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),
|
||||
UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),
|
||||
UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),
|
||||
UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),
|
||||
UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),
|
||||
UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),
|
||||
UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),
|
||||
UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),
|
||||
UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),
|
||||
UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),
|
||||
};
|
||||
|
||||
/**
|
||||
* Reflect all bits of a \a data word of \a data_len bytes.
|
||||
*
|
||||
* \param data The data word to be reflected.
|
||||
* \param data_len The width of \a data expressed in number of bits.
|
||||
* \return The reflected data.
|
||||
*****************************************************************************/
|
||||
static inline uint_fast64_t crc_reflect(uint_fast64_t data, size_t data_len) {
|
||||
uint_fast64_t ret = data & 0x01;
|
||||
|
||||
for (size_t i = 1; i < data_len; i++) {
|
||||
data >>= 1;
|
||||
ret = (ret << 1) | (data & 0x01);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the crc value with new data.
|
||||
*
|
||||
* \param crc The current crc value.
|
||||
* \param data Pointer to a buffer of \a data_len bytes.
|
||||
* \param data_len Number of bytes in the \a data buffer.
|
||||
* \return The updated crc value.
|
||||
******************************************************************************/
|
||||
uint64_t _crc64(uint_fast64_t crc, const void *in_data, const uint64_t len) {
|
||||
const uint8_t *data = in_data;
|
||||
unsigned long long bit;
|
||||
|
||||
for (uint64_t offset = 0; offset < len; offset++) {
|
||||
uint8_t c = data[offset];
|
||||
for (uint_fast8_t i = 0x01; i & 0xff; i <<= 1) {
|
||||
bit = crc & 0x8000000000000000;
|
||||
if (c & i) {
|
||||
bit = !bit;
|
||||
}
|
||||
|
||||
crc <<= 1;
|
||||
if (bit) {
|
||||
crc ^= POLY;
|
||||
}
|
||||
}
|
||||
|
||||
crc &= 0xffffffffffffffff;
|
||||
}
|
||||
|
||||
crc = crc & 0xffffffffffffffff;
|
||||
return crc_reflect(crc, 64) ^ 0x0000000000000000;
|
||||
}
|
||||
|
||||
/******************** END GENERATED PYCRC FUNCTIONS ********************/
|
||||
|
||||
/* Initializes the 16KB lookup tables. */
|
||||
void crc64_init(void) {
|
||||
crcspeed64native_init(_crc64, crc64_table);
|
||||
}
|
||||
|
||||
/* Compute crc64 */
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
|
||||
return crcspeed64native(crc64_table, crc, (void *) s, l);
|
||||
uint64_t j;
|
||||
|
||||
for (j = 0; j < l; j++) {
|
||||
uint8_t byte = s[j];
|
||||
crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Test main */
|
||||
@@ -130,31 +188,8 @@ uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
|
||||
int crc64Test(int argc, char *argv[]) {
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
crc64_init();
|
||||
printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
|
||||
(uint64_t)_crc64(0, "123456789", 9));
|
||||
printf("[64speed]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
|
||||
(uint64_t)crc64(0, "123456789", 9));
|
||||
char li[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed "
|
||||
"do eiusmod tempor incididunt ut labore et dolore magna "
|
||||
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
|
||||
"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis "
|
||||
"aute irure dolor in reprehenderit in voluptate velit esse "
|
||||
"cillum dolore eu fugiat nulla pariatur. Excepteur sint "
|
||||
"occaecat cupidatat non proident, sunt in culpa qui officia "
|
||||
"deserunt mollit anim id est laborum.";
|
||||
printf("[calcula]: c7794709e69683b3 == %016" PRIx64 "\n",
|
||||
(uint64_t)_crc64(0, li, sizeof(li)));
|
||||
printf("[64speed]: c7794709e69683b3 == %016" PRIx64 "\n",
|
||||
(uint64_t)crc64(0, li, sizeof(li)));
|
||||
printf("e9c6d914c4b8d9ca == %016llx\n",
|
||||
(unsigned long long) crc64(0,(unsigned char*)"123456789",9));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef REDIS_TEST_MAIN
|
||||
int main(int argc, char *argv[]) {
|
||||
return crc64Test(argc, argv);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -7,7 +7,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void crc64_init(void);
|
||||
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
|
||||
|
||||
#ifdef REDIS_TEST
|
||||
|
||||
-281
@@ -1,281 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Mark Adler
|
||||
* Originally by: crc64.c Version 1.4 16 Dec 2013 Mark Adler
|
||||
* Modifications by Matt Stancliff <matt@genges.com>:
|
||||
* - removed CRC64-specific behavior
|
||||
* - added generation of lookup tables by parameters
|
||||
* - removed inversion of CRC input/result
|
||||
* - removed automatic initialization in favor of explicit initialization
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the author be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Mark Adler
|
||||
madler@alumni.caltech.edu
|
||||
*/
|
||||
|
||||
#include "crcspeed.h"
|
||||
|
||||
/* Fill in a CRC constants table. */
|
||||
void crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {
|
||||
uint64_t crc;
|
||||
|
||||
/* generate CRCs for all single byte sequences */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
table[0][n] = crcfn(0, &n, 1);
|
||||
}
|
||||
|
||||
/* generate nested CRC table for future slice-by-8 lookup */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
crc = table[0][n];
|
||||
for (int k = 1; k < 8; k++) {
|
||||
crc = table[0][crc & 0xff] ^ (crc >> 8);
|
||||
table[k][n] = crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crcspeed16little_init(crcfn16 crcfn, uint16_t table[8][256]) {
|
||||
uint16_t crc;
|
||||
|
||||
/* generate CRCs for all single byte sequences */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
table[0][n] = crcfn(0, &n, 1);
|
||||
}
|
||||
|
||||
/* generate nested CRC table for future slice-by-8 lookup */
|
||||
for (int n = 0; n < 256; n++) {
|
||||
crc = table[0][n];
|
||||
for (int k = 1; k < 8; k++) {
|
||||
crc = table[0][(crc >> 8) & 0xff] ^ (crc << 8);
|
||||
table[k][n] = crc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reverse the bytes in a 64-bit word. */
|
||||
static inline uint64_t rev8(uint64_t a) {
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
return __builtin_bswap64(a);
|
||||
#else
|
||||
uint64_t m;
|
||||
|
||||
m = UINT64_C(0xff00ff00ff00ff);
|
||||
a = ((a >> 8) & m) | (a & m) << 8;
|
||||
m = UINT64_C(0xffff0000ffff);
|
||||
a = ((a >> 16) & m) | (a & m) << 16;
|
||||
return a >> 32 | a << 32;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* This function is called once to initialize the CRC table for use on a
|
||||
big-endian architecture. */
|
||||
void crcspeed64big_init(crcfn64 fn, uint64_t big_table[8][256]) {
|
||||
/* Create the little endian table then reverse all the entires. */
|
||||
crcspeed64little_init(fn, big_table);
|
||||
for (int k = 0; k < 8; k++) {
|
||||
for (int n = 0; n < 256; n++) {
|
||||
big_table[k][n] = rev8(big_table[k][n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void crcspeed16big_init(crcfn16 fn, uint16_t big_table[8][256]) {
|
||||
/* Create the little endian table then reverse all the entires. */
|
||||
crcspeed16little_init(fn, big_table);
|
||||
for (int k = 0; k < 8; k++) {
|
||||
for (int n = 0; n < 256; n++) {
|
||||
big_table[k][n] = rev8(big_table[k][n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate a non-inverted CRC multiple bytes at a time on a little-endian
|
||||
* architecture. If you need inverted CRC, invert *before* calling and invert
|
||||
* *after* calling.
|
||||
* 64 bit crc = process 8 bytes at once;
|
||||
*/
|
||||
uint64_t crcspeed64little(uint64_t little_table[8][256], uint64_t crc,
|
||||
void *buf, size_t len) {
|
||||
unsigned char *next = buf;
|
||||
|
||||
/* process individual bytes until we reach an 8-byte aligned pointer */
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
/* fast middle processing, 8 bytes (aligned!) per loop */
|
||||
while (len >= 8) {
|
||||
crc ^= *(uint64_t *)next;
|
||||
crc = little_table[7][crc & 0xff] ^
|
||||
little_table[6][(crc >> 8) & 0xff] ^
|
||||
little_table[5][(crc >> 16) & 0xff] ^
|
||||
little_table[4][(crc >> 24) & 0xff] ^
|
||||
little_table[3][(crc >> 32) & 0xff] ^
|
||||
little_table[2][(crc >> 40) & 0xff] ^
|
||||
little_table[1][(crc >> 48) & 0xff] ^
|
||||
little_table[0][crc >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
/* process remaining bytes (can't be larger than 8) */
|
||||
while (len) {
|
||||
crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
uint16_t crcspeed16little(uint16_t little_table[8][256], uint16_t crc,
|
||||
void *buf, size_t len) {
|
||||
unsigned char *next = buf;
|
||||
|
||||
/* process individual bytes until we reach an 8-byte aligned pointer */
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
/* fast middle processing, 8 bytes (aligned!) per loop */
|
||||
while (len >= 8) {
|
||||
uint64_t n = *(uint64_t *)next;
|
||||
crc = little_table[7][(n & 0xff) ^ ((crc >> 8) & 0xff)] ^
|
||||
little_table[6][((n >> 8) & 0xff) ^ (crc & 0xff)] ^
|
||||
little_table[5][(n >> 16) & 0xff] ^
|
||||
little_table[4][(n >> 24) & 0xff] ^
|
||||
little_table[3][(n >> 32) & 0xff] ^
|
||||
little_table[2][(n >> 40) & 0xff] ^
|
||||
little_table[1][(n >> 48) & 0xff] ^
|
||||
little_table[0][n >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
/* process remaining bytes (can't be larger than 8) */
|
||||
while (len) {
|
||||
crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
/* Calculate a non-inverted CRC eight bytes at a time on a big-endian
|
||||
* architecture.
|
||||
*/
|
||||
uint64_t crcspeed64big(uint64_t big_table[8][256], uint64_t crc, void *buf,
|
||||
size_t len) {
|
||||
unsigned char *next = buf;
|
||||
|
||||
crc = rev8(crc);
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
while (len >= 8) {
|
||||
crc ^= *(uint64_t *)next;
|
||||
crc = big_table[0][crc & 0xff] ^
|
||||
big_table[1][(crc >> 8) & 0xff] ^
|
||||
big_table[2][(crc >> 16) & 0xff] ^
|
||||
big_table[3][(crc >> 24) & 0xff] ^
|
||||
big_table[4][(crc >> 32) & 0xff] ^
|
||||
big_table[5][(crc >> 40) & 0xff] ^
|
||||
big_table[6][(crc >> 48) & 0xff] ^
|
||||
big_table[7][crc >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return rev8(crc);
|
||||
}
|
||||
|
||||
/* WARNING: Completely untested on big endian architecture. Possibly broken. */
|
||||
uint16_t crcspeed16big(uint16_t big_table[8][256], uint16_t crc_in, void *buf,
|
||||
size_t len) {
|
||||
unsigned char *next = buf;
|
||||
uint64_t crc = crc_in;
|
||||
|
||||
crc = rev8(crc);
|
||||
while (len && ((uintptr_t)next & 7) != 0) {
|
||||
crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
while (len >= 8) {
|
||||
uint64_t n = *(uint64_t *)next;
|
||||
crc = big_table[0][(n & 0xff) ^ ((crc >> (56 - 8)) & 0xff)] ^
|
||||
big_table[1][((n >> 8) & 0xff) ^ (crc & 0xff)] ^
|
||||
big_table[2][(n >> 16) & 0xff] ^
|
||||
big_table[3][(n >> 24) & 0xff] ^
|
||||
big_table[4][(n >> 32) & 0xff] ^
|
||||
big_table[5][(n >> 40) & 0xff] ^
|
||||
big_table[6][(n >> 48) & 0xff] ^
|
||||
big_table[7][n >> 56];
|
||||
next += 8;
|
||||
len -= 8;
|
||||
}
|
||||
|
||||
while (len) {
|
||||
crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);
|
||||
len--;
|
||||
}
|
||||
|
||||
return rev8(crc);
|
||||
}
|
||||
|
||||
/* Return the CRC of buf[0..len-1] with initial crc, processing eight bytes
|
||||
at a time using passed-in lookup table.
|
||||
This selects one of two routines depending on the endianess of
|
||||
the architecture. */
|
||||
uint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,
|
||||
size_t len) {
|
||||
uint64_t n = 1;
|
||||
|
||||
return *(char *)&n ? crcspeed64little(table, crc, buf, len)
|
||||
: crcspeed64big(table, crc, buf, len);
|
||||
}
|
||||
|
||||
uint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,
|
||||
size_t len) {
|
||||
uint64_t n = 1;
|
||||
|
||||
return *(char *)&n ? crcspeed16little(table, crc, buf, len)
|
||||
: crcspeed16big(table, crc, buf, len);
|
||||
}
|
||||
|
||||
/* Initialize CRC lookup table in architecture-dependent manner. */
|
||||
void crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]) {
|
||||
uint64_t n = 1;
|
||||
|
||||
*(char *)&n ? crcspeed64little_init(fn, table)
|
||||
: crcspeed64big_init(fn, table);
|
||||
}
|
||||
|
||||
void crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]) {
|
||||
uint64_t n = 1;
|
||||
|
||||
*(char *)&n ? crcspeed16little_init(fn, table)
|
||||
: crcspeed16big_init(fn, table);
|
||||
}
|
||||
Loaded 100 of 302 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user