Compare commits
491 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
795b328bd6 | |
|
|
ac5f4a584f | |
|
|
c044906e63 | |
|
|
ccc84945d3 | |
|
|
c4b6f1fcb5 | |
|
|
017ca1efe7 | |
|
|
71b2229427 | |
|
|
446fa6b815 | |
|
|
98948c11bf | |
|
|
d60b141a21 | |
|
|
86bab518c6 | |
|
|
4b9ef52da2 | |
|
|
e8a9967801 | |
|
|
96c896b09a | |
|
|
8d9feff6e2 | |
|
|
cbcd38a9af | |
|
|
cd55792a89 | |
|
|
511b1066c4 | |
|
|
ddf6b5dfe9 | |
|
|
23cd03bc76 | |
|
|
065184a203 | |
|
|
de80dc19dc | |
|
|
dac653d694 | |
|
|
d5dbe3d030 | |
|
|
7973d81b69 | |
|
|
4576f22e24 | |
|
|
cb6f6f13f3 | |
|
|
012b2038ee | |
|
|
48a0ea2e30 | |
|
|
aa48e3cd9a | |
|
|
47fa7f99df | |
|
|
ec8e47f831 | |
|
|
d9d6255621 | |
|
|
2f75958a4a | |
|
|
a4cae13e07 | |
|
|
f136821c0d | |
|
|
b97f23ced2 | |
|
|
84a1f90a9a | |
|
|
3f19715e45 | |
|
|
ce4c5e23d4 | |
|
|
d59da03d60 | |
|
|
8e51f4de3e | |
|
|
0afae48142 | |
|
|
158924f259 | |
|
|
bf7c6e5144 | |
|
|
6f4b3b7cd8 | |
|
|
b1f3c7894e | |
|
|
ec2db7cd33 | |
|
|
63ef9aa8f4 | |
|
|
1d2f938a30 | |
|
|
e4898affdd | |
|
|
558670bc63 | |
|
|
cf16c41054 | |
|
|
7fbb5c620b | |
|
|
9acc202481 | |
|
|
dd0d4dbc19 | |
|
|
a1ae1cbcb0 | |
|
|
fda2679435 | |
|
|
f0484579e0 | |
|
|
6a18162a12 | |
|
|
8e834f7165 | |
|
|
a256976600 | |
|
|
62a29b55a5 | |
|
|
a6dc24e2f3 | |
|
|
70f6ad56ed | |
|
|
7f1a85b025 | |
|
|
eafb8aae74 | |
|
|
de1e9e7ec4 | |
|
|
aa144b5981 | |
|
|
a0654b83c0 | |
|
|
19dcd0c5c4 | |
|
|
d764571f6e | |
|
|
13fc696432 | |
|
|
e4a9fccddb | |
|
|
5477593ce8 | |
|
|
ce0c23358f | |
|
|
f4020e7622 | |
|
|
ed65725833 | |
|
|
13e213e3f8 | |
|
|
83f757672b | |
|
|
21366a4a2e | |
|
|
aad48cc03d | |
|
|
7a108ec5a2 | |
|
|
98e3471433 | |
|
|
8a97e84e1c | |
|
|
8ce0119aa2 | |
|
|
1b777792ab | |
|
|
2ab381d5e3 | |
|
|
a0f1ac59c8 | |
|
|
13b31434e0 | |
|
|
8aceec92ee | |
|
|
fef0a4a7f3 | |
|
|
7f699688c0 | |
|
|
e0f47116c5 | |
|
|
6cd9d6d842 | |
|
|
f4fef93061 | |
|
|
226fd0d378 | |
|
|
9a720533ce | |
|
|
55ff532f92 | |
|
|
d18ad54c2b | |
|
|
2205cae6f3 | |
|
|
5bceed4e32 | |
|
|
e0e7b9ef35 | |
|
|
05f3d62ee1 | |
|
|
01f35da9d2 | |
|
|
02f4659a32 | |
|
|
7816a28c53 | |
|
|
3f5eed3af2 | |
|
|
c9083ae0cf | |
|
|
5283a55bda | |
|
|
78caf9224b | |
|
|
dd77da144f | |
|
|
700a5bcaec | |
|
|
4eb30adb1a | |
|
|
06f0ce67fd | |
|
|
7018f4e337 | |
|
|
d68c9aff32 | |
|
|
89e70977d5 | |
|
|
5bda2dfef6 | |
|
|
8b545fcfc0 | |
|
|
6843acb456 | |
|
|
894477753c | |
|
|
6a5d4b1402 | |
|
|
1c1ea296e7 | |
|
|
961ea11c4f | |
|
|
718906e477 | |
|
|
eafe769759 | |
|
|
a1914ea249 | |
|
|
61e07a4c0f | |
|
|
6151ea721b | |
|
|
255defa788 | |
|
|
acaf94db0c | |
|
|
2e176a9d1b | |
|
|
e0b93756ab | |
|
|
7b0631e37a | |
|
|
e0bbe89d23 | |
|
|
1c1cb6d5e7 | |
|
|
70c44ea490 | |
|
|
b4e5a26ba0 | |
|
|
f49c5d134a | |
|
|
db49d54a37 | |
|
|
c9687df03a | |
|
|
d24023aee7 | |
|
|
5139ec4682 | |
|
|
bc3a65ee19 | |
|
|
44f827f291 | |
|
|
4a86162913 | |
|
|
d5f6a192a5 | |
|
|
05a694df77 | |
|
|
45e958cc1c | |
|
|
d9c14d602a | |
|
|
4480669e03 | |
|
|
fafbbb557e | |
|
|
1969fbba8d | |
|
|
896b35603f | |
|
|
15facf395b | |
|
|
3924213c9a | |
|
|
643ecdc906 | |
|
|
7726d0223f | |
|
|
4df1107fa3 | |
|
|
0c588d965e | |
|
|
8de24ad5b5 | |
|
|
8cea8632d7 | |
|
|
ccbc201636 | |
|
|
e02bb4edc3 | |
|
|
085e06aac1 | |
|
|
0806435fcc | |
|
|
c00a159fd3 | |
|
|
5247caa71a | |
|
|
2c5734ab22 | |
|
|
a19119f962 | |
|
|
428d13a07b | |
|
|
6dbc8b8937 | |
|
|
f1691b7480 | |
|
|
595a8c5664 | |
|
|
8b298570b3 | |
|
|
d81b75e4da | |
|
|
ee3631d426 | |
|
|
6d3d082c6d | |
|
|
c5138cc7cf | |
|
|
73ab695fc8 | |
|
|
be9c7c394a | |
|
|
0113263e87 | |
|
|
fd8fbfe644 | |
|
|
8ab3420bce | |
|
|
aa2fc62cbb | |
|
|
ee70efcd09 | |
|
|
86288bf99e | |
|
|
c0ea8349d3 | |
|
|
8ea9848047 | |
|
|
d07058934b | |
|
|
8e4adccb83 | |
|
|
1eb603cfd1 | |
|
|
22c7e31752 | |
|
|
c18e864220 | |
|
|
7289215470 | |
|
|
b52e516aee | |
|
|
9c967d8d4b | |
|
|
2b0796b166 | |
|
|
6ea757f492 | |
|
|
cf66174cf9 | |
|
|
117b723274 | |
|
|
5e8dc1fcb5 | |
|
|
c416c6eeb0 | |
|
|
88ad619902 | |
|
|
47fb7b2abd | |
|
|
6b70919699 | |
|
|
a9b0f20f2d | |
|
|
48ec9c64c6 | |
|
|
b0d57b325d | |
|
|
82bbc013b2 | |
|
|
7e52e7607a | |
|
|
f47f136f56 | |
|
|
e11b22ad7d | |
|
|
ed66409220 | |
|
|
51715f26aa | |
|
|
68360c1bd1 | |
|
|
138e06fd6c | |
|
|
a9e82600b7 | |
|
|
599d0f1446 | |
|
|
4a7d852409 | |
|
|
e254b8e1d3 | |
|
|
fe862d887e | |
|
|
aa5506191f | |
|
|
5a90576bdc | |
|
|
7e5acf144b | |
|
|
0b539bbba2 | |
|
|
009d33480f | |
|
|
5f34bc872b | |
|
|
793d683d2a | |
|
|
64e9499de6 | |
|
|
275279a264 | |
|
|
f24c558761 | |
|
|
1f83e66306 | |
|
|
630a6a2af6 | |
|
|
23628e85f2 | |
|
|
082902d28b | |
|
|
5802203f9f | |
|
|
f836b1a9e2 | |
|
|
26abf68ebd | |
|
|
ba011767f8 | |
|
|
7d5e94f9dd | |
|
|
80b751980b | |
|
|
776a875c84 | |
|
|
3fd3b167a8 | |
|
|
08f74db732 | |
|
|
6836337d0a | |
|
|
1cb1847b29 | |
|
|
c9cba5298a | |
|
|
7b9c257652 | |
|
|
9f1d1b6d48 | |
|
|
3331b927b3 | |
|
|
e9d186ce99 | |
|
|
29cc5af134 | |
|
|
21cd3a9798 | |
|
|
3f20211a52 | |
|
|
a19a0071db | |
|
|
787eb3a7d6 | |
|
|
4b9e2d6517 | |
|
|
505893dc4c | |
|
|
34f8c3abb9 | |
|
|
a39005253b | |
|
|
013b7888ea | |
|
|
f734710832 | |
|
|
0410c7fedd | |
|
|
0381982ac2 | |
|
|
af68bf5d6a | |
|
|
7fbdb0cece | |
|
|
c3f22aa867 | |
|
|
89e1a99129 | |
|
|
59acf16fae | |
|
|
7fe16fd1b5 | |
|
|
d9097001b5 | |
|
|
d5d317f912 | |
|
|
b4f6152776 | |
|
|
750c2c5e8a | |
|
|
fdb6bfb233 | |
|
|
f6c5256264 | |
|
|
0c437d3799 | |
|
|
64ae0757d7 | |
|
|
720c4a686d | |
|
|
87377981ec | |
|
|
740272e303 | |
|
|
4f29fd71eb | |
|
|
976872192a | |
|
|
b03963d4f6 | |
|
|
ce2ee0f482 | |
|
|
9ca9592326 | |
|
|
09e00bcf5e | |
|
|
a439b8e62d | |
|
|
8acd5e031b | |
|
|
2432e0bc79 | |
|
|
1b35cbc094 | |
|
|
8491c5d155 | |
|
|
8717f85608 | |
|
|
55f1e139a9 | |
|
|
2993b34e80 | |
|
|
81950ac246 | |
|
|
7614f789c8 | |
|
|
f5fa33611f | |
|
|
6957c19992 | |
|
|
a66370d0f8 | |
|
|
e1ef38d6a4 | |
|
|
45da326ce2 | |
|
|
ebe7d87ca4 | |
|
|
c6b2c32fe2 | |
|
|
b7ecd0d4c4 | |
|
|
2fafa215fd | |
|
|
44dcf4f821 | |
|
|
47c4eaeb13 | |
|
|
082a0fbc06 | |
|
|
3e0f1eb1fc | |
|
|
1e9df48fec | |
|
|
75b2aaee97 | |
|
|
de879c15ee | |
|
|
3ad4a89ff5 | |
|
|
3a61bf8074 | |
|
|
df43a7d48b | |
|
|
d5f9271597 | |
|
|
e10bb45605 | |
|
|
a5ee277e2d | |
|
|
ba1f8eb38f | |
|
|
e6be76eb74 | |
|
|
1d1a766c70 | |
|
|
e068391c87 | |
|
|
48f108b40d | |
|
|
d5fd9a176d | |
|
|
41681a5eb7 | |
|
|
da2077c6b8 | |
|
|
8174aed8e3 | |
|
|
ec181d51cf | |
|
|
639d388c6d | |
|
|
768fbb68d4 | |
|
|
6fddce73c5 | |
|
|
df4fdfcab7 | |
|
|
797f4e08b8 | |
|
|
4558290e1e | |
|
|
5650e87665 | |
|
|
63d57f82c6 | |
|
|
082da383ff | |
|
|
9d9cee76ac | |
|
|
1fd4a5da2b | |
|
|
f37c3bf013 | |
|
|
76ef22d380 | |
|
|
6a3bcd4547 | |
|
|
c1bb9d6532 | |
|
|
097a7db3cb | |
|
|
cc304e5d7a | |
|
|
07ba689835 | |
|
|
13de41bc05 | |
|
|
88aa31df07 | |
|
|
04226e28fc | |
|
|
3f15be8d92 | |
|
|
0ed5b405d9 | |
|
|
9665ff87e3 | |
|
|
02701c3781 | |
|
|
12aab5f94c | |
|
|
35aa996333 | |
|
|
41a4ca78c7 | |
|
|
e104d2f7f6 | |
|
|
aea8a6562d | |
|
|
c822e92b19 | |
|
|
1f126476bc | |
|
|
3b2b9d6176 | |
|
|
10c037e32d | |
|
|
7d5a540a2f | |
|
|
89c68e11db | |
|
|
e96127a040 | |
|
|
89abbd2ee4 | |
|
|
7190fad3d0 | |
|
|
3e192fae09 | |
|
|
0cd65f088c | |
|
|
ff7109fcd4 | |
|
|
85354b6ef6 | |
|
|
5a04c1c656 | |
|
|
0995f447cb | |
|
|
3d74837527 | |
|
|
93e6cec05d | |
|
|
b236808a69 | |
|
|
1189c97d8e | |
|
|
8718818dc0 | |
|
|
f5028fcbe0 | |
|
|
58e1aea32b | |
|
|
5f9bfd8bae | |
|
|
2e85b7ad9d | |
|
|
1ac8c8f3c2 | |
|
|
d54007f79b | |
|
|
bb33b2e09f | |
|
|
5b6bb3193b | |
|
|
56a5ac4f92 | |
|
|
fa8f93b018 | |
|
|
0aab6a0531 | |
|
|
dc40977500 | |
|
|
118f1e2e2e | |
|
|
7d5512682b | |
|
|
082a322f3d | |
|
|
55fc4013b3 | |
|
|
dc43f8254a | |
|
|
556b7337c4 | |
|
|
23d8adf05f | |
|
|
bc5efd235e | |
|
|
6a54076cf5 | |
|
|
488e483d22 | |
|
|
130d7fe110 | |
|
|
a749b84ce2 | |
|
|
d9b914ea6e | |
|
|
98b816c625 | |
|
|
6eb2d1c2e7 | |
|
|
3ff6cd8844 | |
|
|
cc80f721f2 | |
|
|
f776a818f4 | |
|
|
c2d8faaba7 | |
|
|
4b085fbc09 | |
|
|
29586005ba | |
|
|
8e0f5794d9 | |
|
|
4aad4f78b9 | |
|
|
ec22ab681e | |
|
|
fb03bf23ba | |
|
|
473b1e7218 | |
|
|
2cb3214d58 | |
|
|
7187840167 | |
|
|
d1f1bf97e1 | |
|
|
85aff01e5e | |
|
|
b9cae5de7f | |
|
|
7149a54128 | |
|
|
54cb8f8415 | |
|
|
87d7b1f55d | |
|
|
e86767c120 | |
|
|
73dba84e82 | |
|
|
e4a5cadfe1 | |
|
|
c8bf9f24ef | |
|
|
4011fcb39e | |
|
|
688fcf4b74 | |
|
|
759fd117d8 | |
|
|
4e53bd3e2d | |
|
|
01b1841fda | |
|
|
201daee3b6 | |
|
|
c6ddfb98a1 | |
|
|
46da99bbba | |
|
|
0810cb4d21 | |
|
|
427818d005 | |
|
|
590aa8d3a0 | |
|
|
2df796589c | |
|
|
d3b0c5dfb0 | |
|
|
c2837fde5f | |
|
|
da4c9a2581 | |
|
|
6d88bb980d | |
|
|
edb8b2c4dd | |
|
|
21336eee3e | |
|
|
4d653d1b3e | |
|
|
3d0956d302 | |
|
|
f9881d96b7 | |
|
|
b88d5b03fe | |
|
|
6b528f123f | |
|
|
d87d98dbe0 | |
|
|
773f1bc28c | |
|
|
8005b18198 | |
|
|
3e3bcda2df | |
|
|
74bcd17360 | |
|
|
a57b87b556 | |
|
|
faa1b6ddf2 | |
|
|
fd4368e6e2 | |
|
|
6e5511614d | |
|
|
89ad53974f | |
|
|
11a441e1f8 | |
|
|
81e6455819 | |
|
|
7459f7ded4 | |
|
|
5d3c3c576c | |
|
|
d6b0f1b0c4 | |
|
|
798244cc1b | |
|
|
90e61572c0 | |
|
|
a6066181ae | |
|
|
aac2abcfa9 | |
|
|
1c7069068b | |
|
|
91e0124ae5 | |
|
|
916ad1f9e0 | |
|
|
b10c5c6294 | |
|
|
c8c75666cf | |
|
|
12dde730c8 | |
|
|
ad57015174 | |
|
|
2992bfc4be | |
|
|
1302c5306a | |
|
|
41e4e6f079 | |
|
|
dd34251817 | |
|
|
b7dde81d1e | |
|
|
414f20fa61 | |
|
|
22a6115c4e | |
|
|
d818e59009 | |
|
|
938a6a7fac | |
|
|
b89d23ab8b | |
|
|
aabc50c144 |
|
|
@ -0,0 +1,98 @@
|
|||
# Makefile.in
|
||||
src/Makefile
|
||||
src/tests/Makefile
|
||||
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dSYM
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.out
|
||||
src/_os_define.h
|
||||
src/tests/test_allocator
|
||||
src/tests/test_blocked_queue
|
||||
src/tests/test_char_convert
|
||||
src/tests/test_char_convert_loader
|
||||
src/tests/test_crc32
|
||||
src/tests/test_id_generator
|
||||
src/tests/test_ini_parser
|
||||
src/tests/test_json_parser
|
||||
src/tests/test_logger
|
||||
src/tests/test_mblock
|
||||
src/tests/test_multi_skiplist
|
||||
src/tests/test_sched_thread
|
||||
src/tests/test_skiplist
|
||||
src/tests/test_skiplist_set
|
||||
src/tests/test_thourands_seperator
|
||||
src/tests/test_pthread_lock
|
||||
src/tests/test_split_string
|
||||
src/tests/test_uniq_skiplist
|
||||
src/tests/test_server_id_func
|
||||
src/tests/test_pipe
|
||||
src/tests/test_atomic
|
||||
src/tests/test_file_write_hole
|
||||
src/tests/test_file_lock
|
||||
src/tests/test_thread_pool
|
||||
src/tests/test_data_visible
|
||||
src/tests/test_pthread_wait
|
||||
src/tests/test_mutex_lock_perf
|
||||
src/tests/test_queue_perf
|
||||
src/tests/test_normalize_path
|
||||
src/tests/test_sorted_array
|
||||
src/tests/test_sorted_queue
|
||||
src/tests/test_thread_local
|
||||
src/tests/test_memcpy
|
||||
src/tests/mblock_benchmark
|
||||
src/tests/cpool_benchmark
|
||||
src/tests/test_fast_buffer
|
||||
|
||||
# other
|
||||
*.swp
|
||||
*.swo
|
||||
php-fastcommon/.deps
|
||||
php-fastcommon/.libs/
|
||||
|
||||
php-fastcommon/Makefile
|
||||
php-fastcommon/Makefile.fragments
|
||||
php-fastcommon/Makefile.global
|
||||
php-fastcommon/Makefile.objects
|
||||
php-fastcommon/acinclude.m4
|
||||
php-fastcommon/aclocal.m4
|
||||
php-fastcommon/autom4te.cache/
|
||||
php-fastcommon/build/
|
||||
php-fastcommon/config.guess
|
||||
php-fastcommon/config.h
|
||||
php-fastcommon/config.h.in
|
||||
php-fastcommon/config.log
|
||||
php-fastcommon/config.nice
|
||||
php-fastcommon/config.status
|
||||
php-fastcommon/config.sub
|
||||
php-fastcommon/configure
|
||||
php-fastcommon/configure.ac
|
||||
php-fastcommon/install-sh
|
||||
php-fastcommon/libtool
|
||||
php-fastcommon/ltmain.sh
|
||||
php-fastcommon/missing
|
||||
php-fastcommon/mkinstalldirs
|
||||
php-fastcommon/run-tests.php
|
||||
260
HISTORY
260
HISTORY
|
|
@ -1,4 +1,264 @@
|
|||
|
||||
Version 1.83 2025-11-15
|
||||
* fast_task_queue.h: remove field finish_callback
|
||||
|
||||
Version 1.82 2025-11-04
|
||||
* set use_io_uring explicitly
|
||||
|
||||
Version 1.81 2025-10-05
|
||||
* support Linux io_uring
|
||||
* free_queue support parameter: need_shrink and set task->shrinked
|
||||
* IOEventCallback: change event type from short to int
|
||||
|
||||
Version 1.80 2025-09-10
|
||||
* getIpaddrByNameEx: IPv4 has priority over IPv6
|
||||
* shared_func.[hc]: add function fc_ftoa
|
||||
|
||||
Version 1.79 2025-08-29
|
||||
* logger.h export function log_it_ex3
|
||||
* shared_func.[hc]: add function bytes_to_human_str
|
||||
|
||||
Version 1.78 2025-08-07
|
||||
* getIpaddrByName: normalize ip addr when input addr is IPv4 or IPv6
|
||||
* add files: spinlock.[hc]
|
||||
* shared_func.[hc]: change int2buff, buff2int etc. functions to static inline
|
||||
* shared_func.[hc]: add functions short2hex, int2hex, long2hex etc.
|
||||
* performance opt.: replace sprintf and snprintf as necessary
|
||||
|
||||
Version 1.77 2025-03-18
|
||||
* impl. shorten_path for /./ and /../
|
||||
* add function fc_compare_int64_ptr
|
||||
|
||||
Version 1.76 2025-01-27
|
||||
* get_mounted_filesystems act as program df
|
||||
* add function get_statfs_by_path
|
||||
* add function is_rotational_device_by_path
|
||||
* conn_pool_get_connection_ex add parameter: shared
|
||||
|
||||
Version 1.75 2024-09-22
|
||||
* task init callback support extra argument
|
||||
* connection pool performance optimization
|
||||
|
||||
Version 1.74 2024-05-18
|
||||
* add functions: get_log_level and get_log_level_caption
|
||||
* adapt to FreeBSD 13
|
||||
|
||||
Version 1.73 2024-03-05
|
||||
* add macro FC_SET_STRING_EMPTY
|
||||
* struct fast_task_info remove fields: connect_timeout and network_timeout
|
||||
* format ip address for IPv6
|
||||
|
||||
Version 1.72 2024-01-21
|
||||
* call fast_mblock_ref_counter_dec for delay free node correctly
|
||||
* fc_queue.[hc]: add function fc_queue_remove
|
||||
|
||||
Version 1.71 2023-12-23
|
||||
* full support IPv6 by pull request #47
|
||||
* replace inet_ntop to getnameinfo for IPv6
|
||||
|
||||
Version 1.70 2023-09-30
|
||||
* get full mac address of infiniband NIC under Linux
|
||||
* struct fast_task_info add field conn for RDMA connection
|
||||
* server_id_func.[hc]: support communication type
|
||||
* connection_pool.[hc] support callbacks for RDMA
|
||||
* nio thread data support busy_polling_callback
|
||||
* connection_pool.[hc] support thread local for performance
|
||||
* struct fast_task_info support send and recv double buffers
|
||||
* add functions: fc_queue_push_with_check and fc_queue_peek
|
||||
|
||||
Version 1.69 2023-08-05
|
||||
* bugfixed: array_allocator_alloc MUST init the array
|
||||
* uniq_skiplist support arg for free callback
|
||||
|
||||
Version 1.68 2023-07-05
|
||||
* sorted_queue.[hc]: pop_compare_func support argument
|
||||
|
||||
Version 1.67 2023-05-29
|
||||
* lc_pair in struct fc_queue change to lcp
|
||||
* sorted queue use double link chain for quick push
|
||||
* add function uniq_skiplist_clear
|
||||
* fast_mblock_malloc_trunk_notify_func prototype changed
|
||||
* fast_mblock_init_ex2 add parameter prealloc_trunk_count
|
||||
* sorted_queue.[hc] support pop_compare_func
|
||||
* bugfixed: fast_mblock_batch_alloc correct return value
|
||||
* add function fc_safe_writev
|
||||
|
||||
Version 1.66 2023-02-12
|
||||
* struct fast_task_info add field: notify_next for nio notify queue
|
||||
|
||||
Version 1.65 2023-01-09
|
||||
* locked_list.h: add functions locked_list_move and locked_list_move_tail
|
||||
* add function tcp_socket_connected
|
||||
* parse_bytes support space charactors (before and after the unit)
|
||||
* add function get_groups
|
||||
|
||||
Version 1.64 2022-11-19
|
||||
* shared_func.[hc]: normalize_path use type string_t for general purpose
|
||||
* bugfixed: common_blocked_queue_[alloc|free]_node must use lock
|
||||
* bugfixed: can't use global malloc_allocator
|
||||
|
||||
Version 1.63 2022-10-16
|
||||
* sockopt.[hc]: getIpAndPort support ipv6
|
||||
|
||||
Version 1.62 2022-09-28
|
||||
* add function fc_sleep_us
|
||||
* add function fc_itoa
|
||||
|
||||
Version 1.61 2022-09-21
|
||||
* get_base_path_from_conf_file_ex support parameter: noent_log_level
|
||||
* add function common_blocked_queue_push_chain
|
||||
|
||||
Version 1.60 2022-08-27
|
||||
* normalize_path for base_path
|
||||
* struct fast_task_info add field recv_body for dynamic recv buffer
|
||||
* add functions: iniGetDoubleCorrectValueEx and iniGetPercentCorrectValueEx
|
||||
* fast_allocator.[hc] support object size and callbacks
|
||||
|
||||
Version 1.59 2022-07-21
|
||||
* open file with flag O_CLOEXEC
|
||||
* add global var g_set_cloexec and macro FC_SET_CLOEXEC
|
||||
* add function fc_get_first_lines
|
||||
|
||||
Version 1.58 2022-06-04
|
||||
* add function conn_pool_connect_server_ex1 to support service name
|
||||
* add function conn_pool_get_connection_ex to support service name
|
||||
* add function iniGetCharValueEx
|
||||
* json_parser.[hc] refined for better performance
|
||||
|
||||
Version 1.57 2022-04-22
|
||||
* add function fc_format_path
|
||||
* add functions: fc_get_path_child_count and fc_copy_file
|
||||
* fast_mblock.[hc] support object destroy callback
|
||||
* bugfixed: fc_get_file_line_count_ex should rewind file
|
||||
|
||||
Version 1.56 2022-03-09
|
||||
* add function fc_gettid
|
||||
* function normalize_path: NULL from parameter for getcwd
|
||||
* sockopt.[hc] support tcpwritev and tcpreadv
|
||||
* add function fc_iov_get_bytes
|
||||
* rename hash_xxx to fc_hash_xxx
|
||||
* rename trim to fc_trim
|
||||
* add function fc_check_rename_ex
|
||||
|
||||
Version 1.55 2022-01-12
|
||||
* fastcommon php extension adapt to php 8
|
||||
* function fast_mblock_batch_alloc changed
|
||||
* add function sched_delay_free_ptr
|
||||
|
||||
Version 1.54 2021-12-23
|
||||
* fast_allocator.[hc]: correct reclaim_interval logic
|
||||
* shared_func.[hc]: add functions getFileContentEx1 and getFileContent1
|
||||
* fc_queue.[hc]: add function fc_queue_timedpeek
|
||||
* pthread_func.[hc]: add function init_pthread_rwlock
|
||||
* add files: sorted_queue.[hc]
|
||||
* add files: array_allocator.[hc]
|
||||
* add files: sorted_array.[hc]
|
||||
* shared_func.[hc]: add function fc_read_lines
|
||||
* normalize_path removes prefix one ./ and multi ../
|
||||
|
||||
Version 1.53 2021-06-30
|
||||
* process_action support action status
|
||||
* uniq_skiplist.h: add function uniq_skiplist_iterator_at
|
||||
|
||||
Version 1.52 2021-06-08
|
||||
* process_stop more gracefully (kill -9 on timeout)
|
||||
* add function fc_queue_pop_to_queue_ex
|
||||
* add function get_path_block_size and get_device_block_size
|
||||
* add function fc_check_realloc_iovec_array
|
||||
|
||||
Version 1.51 2021-05-27
|
||||
* fast_mblock.[hc]: support batch alloc and batch free
|
||||
* uniq_skiplist.[hc]: init function add parameter: allocator_use_lock
|
||||
* add function normalize_path_ex and normalize_uri
|
||||
* use libcurl to fetch URL resource
|
||||
* add function get_kernel_version
|
||||
|
||||
Version 1.50 2021-05-11
|
||||
* add function is_digital_string
|
||||
* add function fc_server_load_from_ini_context
|
||||
* set thread name as necessary
|
||||
* add macro fc_fallocate
|
||||
|
||||
Version 1.49 2021-04-17
|
||||
* add macros: FC_ABS and FC_NEGATIVE
|
||||
* uniq_skiplist.c: add uniq_skiplist_pair struct and init function
|
||||
* add functions: fc_mkdirs and str_replace
|
||||
* add FilenameString type and macro
|
||||
* add functions: fc_string_case_compare, fc_string_case_equal etc.
|
||||
* add function: fc_check_filename_ex
|
||||
* add functions: fc_queue_push_queue_to_tail etc.
|
||||
* add file locked_list.h
|
||||
* parse_bytes function more graceful
|
||||
|
||||
Version 1.48 2021-02-01
|
||||
* fast_buffer.[hc]: add function fast_buffer_append_binary
|
||||
* fc_check_mkdir_ex return 0 when mkdir with errno EEXIST
|
||||
* add function common_blocked_queue_timedpop
|
||||
* add function fc_queue_timedpop
|
||||
|
||||
Version 1.47 2021-01-20
|
||||
* fc_atomic.h: add FC_ATOMIC_GET and FC_ATOMIC_SET etc.
|
||||
* fast_mblock.[hc]: support wait with element limit
|
||||
|
||||
Version 1.46 2020-12-29
|
||||
* add function get_time_item_from_conf_ex
|
||||
* INI_FIND_ITEM just break instead of return
|
||||
|
||||
Version 1.45 2020-12-24
|
||||
* add function uniq_skiplist_find_node_ex
|
||||
* use readdir instead of readdir_r in Linux
|
||||
|
||||
Version 1.44 2020-12-06
|
||||
* add test file src/tests/test_pthread_lock.c
|
||||
* add uniq_skiplist.[hc]
|
||||
* add function split_string_ex
|
||||
* fast_mblock.[hc]: add init_args for init_func
|
||||
* struct fast_task_info add field: nio_stage
|
||||
* add function fc_memrchr
|
||||
* add function is_network_error
|
||||
* add function fast_mpool_log_stats
|
||||
* add files: server_id_func.[hc]
|
||||
* common_blocked_queue support pop all nodes
|
||||
* shared_func.[hc]: add functions fc_floor_prime and fc_ceil_prime
|
||||
* fast_mpool.[hc]: change function fast_mpool_strdup
|
||||
* fast_allocator.[hc]: add function fast_allocator_strdup
|
||||
* shared_func.[hc]: add function getFileSize
|
||||
* char_converter.[hc]: add function fast_char_unescape
|
||||
* struct fast_task_info add ctx pointer for libserverframe nio
|
||||
* struct thread_data add waiting_queue for Linux eventfd notify
|
||||
* struct fast_task_info add canceled field for complicated thread model
|
||||
* nio_thread_data support thread notify
|
||||
* pthread_func.[hc] add functions: create_work_threads_ex and fc_create_thread
|
||||
* sched_add_entries use temp ScheduleArray for rare case
|
||||
* add function common_blocked_queue_return_nodes
|
||||
* add functions getIpAndPort and getPeerIpAndPort
|
||||
* bugfixed: call fast_mblock_destroy in common_blocked_queue_destroy
|
||||
* add function fc_get_file_line_count_ex
|
||||
* uniq_skiplist add function find_ge and support bidirection
|
||||
* connection_pool support validate connection on error
|
||||
* fast_task_queue.[hc]: free_queue support init_callback
|
||||
* ini_file_reader.c: use mutex lock when access dynamic content array
|
||||
* uniq_skiplist add function uniq_skiplist_replace_ex
|
||||
* add files: fc_queue.[hc]
|
||||
* add files: fc_memory.[hc]
|
||||
* add files: shared_buffer.[hc]
|
||||
* add files: thread_pool.[hc]
|
||||
* shared_func.[hc]: add function fc_path_contains
|
||||
* fast_mblock.[hc]: support alloc elements limit
|
||||
* sockopt.[hc]: add function asyncconnectserverbyip
|
||||
* add locked_timer.[hc]: time wheel timer with lock
|
||||
* tcp_quick_ack option for Linux
|
||||
|
||||
Version 1.43 2019-12-25
|
||||
* replace function call system to getExecResult,
|
||||
system is the deprecated function in iOS 11
|
||||
* correct function skiplist_iterator in skiplist.h
|
||||
* add buffered_file_writer.[hc]
|
||||
* add function fc_delete_file_ex
|
||||
|
||||
Version 1.42 2019-12-03
|
||||
* add function get_gzip_command_filename
|
||||
* function create_work_threads changed
|
||||
|
||||
Version 1.41 2019-09-30
|
||||
* change CIDR network_bits range from [16, 32) to [10, 32)
|
||||
|
|
|
|||
30
INSTALL
30
INSTALL
|
|
@ -4,20 +4,22 @@ libfastcommon may be copied only under the terms of the Less GNU General
|
|||
Public License(LGPL).
|
||||
Please visit the libfastcommon Home Page for more detail.
|
||||
English language: https://github.com/happyfish100/libfastcommon
|
||||
Chinese language: http://www.csource.org/
|
||||
Chinese language: http://www.fastken.com/
|
||||
|
||||
[Optional Step]
|
||||
You can enable io_uring for higher performance when Linux kernel version >= 6.2,
|
||||
CentOS, RockyLinux, AlmaLinux, RHEL etc.:
|
||||
sudo yum install liburing-devel -y
|
||||
|
||||
Debian, Ubuntu etc.:
|
||||
sudo apt install liburing-dev -y
|
||||
|
||||
|
||||
#step 1. download libfastcommon source package and unpack it,
|
||||
tar xzf libfastcommon_v1.x.tar.gz
|
||||
#for example:
|
||||
tar xzf libfastcommon_v1.23.tar.gz
|
||||
|
||||
#step 2. enter the libfastcommon dir
|
||||
cd libfastcommon
|
||||
|
||||
#step 3. make
|
||||
./make.sh
|
||||
|
||||
#step 4. make install
|
||||
./make.sh install
|
||||
# download libfastcommon source codes and install it,
|
||||
# github address: https://github.com/happyfish100/libfastcommon.git
|
||||
# gitee address: https://gitee.com/fastdfs100/libfastcommon.git
|
||||
# the command lines as:
|
||||
|
||||
git clone https://github.com/happyfish100/libfastcommon.git
|
||||
cd libfastcommon; git checkout V1.0.83
|
||||
./make.sh clean && ./make.sh && sudo ./make.sh install
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
2
README
2
README
|
|
@ -4,7 +4,7 @@ libfastcommon may be copied only under the terms of the Less GNU General
|
|||
Public License(LGPL).
|
||||
Please visit the libfastcommon Home Page for more detail.
|
||||
English language: https://github.com/happyfish100/libfastcommon
|
||||
Chinese language: http://www.csource.org/
|
||||
Chinese language: http://www.fastken.com/
|
||||
|
||||
|
||||
c common functions library extracted from my open source projects FastDFS and
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
libfastcommon for Debian
|
||||
-----------------------
|
||||
|
||||
<possible notes regarding this package - if none, delete this file>
|
||||
|
||||
-- zhangchunsheng <zhangchunsheng423@gmail.com> Mon, 24 May 2021 18:54:01 +0800
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
libfastcommon (1.0.83-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.83-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 23 Nov 2025 10:47:37 +0000
|
||||
|
||||
libfastcommon (1.0.83-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.83-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 23 Nov 2025 10:00:00 +0000
|
||||
|
||||
libfastcommon (1.0.83-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.83-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 23 Nov 2025 09:05:57 +0000
|
||||
|
||||
libfastcommon (1.0.78-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.78-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sat, 16 Aug 2025 16:31:05 +0000
|
||||
|
||||
libfastcommon (1.0.77-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.77-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 06 Apr 2025 16:55:50 +0000
|
||||
|
||||
libfastcommon (1.0.75-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.75-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 29 Sep 2024 15:23:57 +0000
|
||||
|
||||
libfastcommon (1.0.74-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.74-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sat, 15 Jun 2024 14:45:01 +0000
|
||||
|
||||
libfastcommon (1.0.73-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.73-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 17 Mar 2024 15:10:21 +0000
|
||||
|
||||
libfastcommon (1.0.72-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.72-1
|
||||
|
||||
-- YuQing <384681@qq.com> Wed, 31 Jan 2024 11:59:25 +0000
|
||||
|
||||
libfastcommon (1.0.71-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.71-1
|
||||
|
||||
-- YuQing <384681@qq.com> Mon, 01 Jan 2024 11:23:54 +0000
|
||||
|
||||
libfastcommon (1.0.70-3) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.70-3
|
||||
|
||||
-- YuQing <384681@qq.com> Tue, 21 Nov 2023 14:35:29 +0000
|
||||
|
||||
libfastcommon (1.0.70-2) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.70-2
|
||||
|
||||
-- YuQing <384681@qq.com> Mon, 20 Nov 2023 13:23:17 +0000
|
||||
|
||||
libfastcommon (1.0.70-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.70-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 19 Nov 2023 14:45:34 +0000
|
||||
|
||||
libfastcommon (1.0.69-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.69-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 06 Aug 2023 07:21:50 +0000
|
||||
|
||||
libfastcommon (1.0.68-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.68-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 23 Jul 2023 14:27:27 +0000
|
||||
|
||||
libfastcommon (1.0.67-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.67-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 04 Jun 2023 10:51:06 +0000
|
||||
|
||||
libfastcommon (1.0.66-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.66-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sat, 18 Feb 2023 05:43:58 +0000
|
||||
|
||||
libfastcommon (1.0.65-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.65-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 15 Jan 2023 13:49:19 +0000
|
||||
|
||||
libfastcommon (1.0.63-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.63-1
|
||||
|
||||
-- YuQing <384681@qq.com> Mon, 21 Nov 2022 14:54:56 +0000
|
||||
|
||||
libfastcommon (1.0.62-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.62-1
|
||||
|
||||
-- YuQing <384681@qq.com> Sat, 08 Oct 2022 13:28:15 +0000
|
||||
|
||||
libfastcommon (1.0.61-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.61-1
|
||||
|
||||
-- YuQing <384681@qq.com> Thu, 22 Sep 2022 12:22:09 +0000
|
||||
|
||||
libfastcommon (1.0.60-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.60-1
|
||||
|
||||
-- YuQing <384681@qq.com> Wed, 07 Sep 2022 13:36:01 +0000
|
||||
|
||||
libfastcommon (1.0.59-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.59-1
|
||||
|
||||
-- YuQing <384681@qq.com> Mon, 25 Jul 2022 13:51:25 +0000
|
||||
|
||||
libfastcommon (1.0.58-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.58-1
|
||||
|
||||
-- YuQing <384681@qq.com> Wed, 15 Jun 2022 14:26:02 +0000
|
||||
|
||||
libfastcommon (1.0.57-1) unstable; urgency=medium
|
||||
|
||||
* upgrade to 1.0.57-1
|
||||
|
||||
-- YuQing <384681@qq.com> Thu, 28 Apr 2022 11:53:21 +0000
|
||||
|
||||
libfastcommon (1.0.56-1) unstable; urgency=medium
|
||||
|
||||
* check pthread_rwlockattr_getkind_np for porting
|
||||
* correct pthread_rwlockattr getkind_np to setkind_np
|
||||
* change include <sys/poll.h> to #include <poll.h>
|
||||
* add function fc_gettid
|
||||
* NULL from parameter for getcwd
|
||||
* sockopt.[hc] support tcpwritev and tcpreadv
|
||||
* add const modifier for unification
|
||||
* make.sh refined
|
||||
* add function fc_iov_get_bytes
|
||||
* make.sh: generate macros for dirent fields
|
||||
* add function log_try_init2
|
||||
* rename hash_xxx to fc_hash_xxx
|
||||
* rename trim to fc_trim
|
||||
* php-fastcommon compile OK
|
||||
* add function locked_list_destroy
|
||||
* upgrade version to 1.0.56
|
||||
* add function fc_check_rename_ex
|
||||
* small changes for logger.[hc]
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 13 Mar 2022 16:43:08 +0800
|
||||
|
||||
libfastcommon (1.0.55-1) unstable; urgency=medium
|
||||
|
||||
* upgrade version to 1.0.55
|
||||
|
||||
-- YuQing <384681@qq.com> Sat, 15 Jan 2022 19:50:26 +0800
|
||||
|
||||
libfastcommon (1.0.54-1) unstable; urgency=medium
|
||||
|
||||
* upgrade version to 1.0.54
|
||||
|
||||
-- YuQing <384681@qq.com> Sun, 26 Dec 2021 20:29:05 +0800
|
||||
|
||||
libfastcommon (1.0.53-1) unstable; urgency=medium
|
||||
|
||||
* open for write MUST have the third parameter: mode
|
||||
* uniq_skiplist.h: add function uniq_skiplist_iterator_at
|
||||
* process_action support action status
|
||||
|
||||
-- YuQing <384681@qq.com> Tue, 6 Jul 2021 21:23:31 +0800
|
||||
|
|
@ -0,0 +1 @@
|
|||
11
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
Source: libfastcommon
|
||||
Section: libs
|
||||
Priority: optional
|
||||
Maintainer: YuQing <384681@qq.com>
|
||||
Build-Depends: debhelper (>=11~)
|
||||
Standards-Version: 4.1.4
|
||||
Homepage: http://github.com/happyfish100/libfastcommon/
|
||||
|
||||
Package: libfastcommon-dev
|
||||
Architecture: any
|
||||
Section: libdevel
|
||||
Depends: libfastcommon (= ${binary:Version}), ${misc:Depends}
|
||||
Description: libfastcommon (development files)
|
||||
This package contains header files.
|
||||
|
||||
Package: libfastcommon
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: c common functions library
|
||||
c common functions library extracted from my open source projects FastDFS and
|
||||
FastDHT. this library is very simple and stable.
|
||||
.
|
||||
some functions are wrappered into php extension, such as fastcommon_gethostaddrs,
|
||||
fastcommon_id_generator_xxx, fastcommon_get_ifconfigs, fastcommon_get_sysinfo etc.
|
||||
|
||||
|
|
@ -0,0 +1,530 @@
|
|||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: libfastcommon
|
||||
Source: http://github.com/happyfish100/libfastcommon/
|
||||
#
|
||||
# Please double check copyright with the licensecheck(1) command.
|
||||
|
||||
Files: php-fastcommon/fastcommon.c
|
||||
php-fastcommon/fastcommon.h
|
||||
src/avl_tree.c
|
||||
src/avl_tree.h
|
||||
src/base64.c
|
||||
src/buffered_file_writer.c
|
||||
src/buffered_file_writer.h
|
||||
src/chain.c
|
||||
src/chain.h
|
||||
src/char_convert_loader.c
|
||||
src/char_convert_loader.h
|
||||
src/char_converter.c
|
||||
src/char_converter.h
|
||||
src/common_blocked_queue.c
|
||||
src/common_blocked_queue.h
|
||||
src/common_define.h
|
||||
src/connection_pool.c
|
||||
src/connection_pool.h
|
||||
src/fast_allocator.c
|
||||
src/fast_allocator.h
|
||||
src/fast_blocked_queue.c
|
||||
src/fast_blocked_queue.h
|
||||
src/fast_buffer.c
|
||||
src/fast_buffer.h
|
||||
src/fast_mblock.c
|
||||
src/fast_mblock.h
|
||||
src/fast_mpool.c
|
||||
src/fast_mpool.h
|
||||
src/fast_task_queue.c
|
||||
src/fast_task_queue.h
|
||||
src/fast_timer.c
|
||||
src/fast_timer.h
|
||||
src/fc_atomic.h
|
||||
src/fc_memory.c
|
||||
src/fc_memory.h
|
||||
src/fc_queue.c
|
||||
src/fc_queue.h
|
||||
src/flat_skiplist.c
|
||||
src/flat_skiplist.h
|
||||
src/hash.c
|
||||
src/hash.h
|
||||
src/http_func.h
|
||||
src/id_generator.c
|
||||
src/ini_file_reader.c
|
||||
src/ini_file_reader.h
|
||||
src/ioevent.c
|
||||
src/ioevent.h
|
||||
src/ioevent_loop.c
|
||||
src/ioevent_loop.h
|
||||
src/json_parser.c
|
||||
src/json_parser.h
|
||||
src/local_ip_func.c
|
||||
src/local_ip_func.h
|
||||
src/locked_timer.c
|
||||
src/locked_timer.h
|
||||
src/logger.c
|
||||
src/multi_skiplist.c
|
||||
src/multi_skiplist.h
|
||||
src/multi_socket_client.c
|
||||
src/multi_socket_client.h
|
||||
src/process_ctrl.c
|
||||
src/process_ctrl.h
|
||||
src/pthread_func.c
|
||||
src/pthread_func.h
|
||||
src/sched_thread.c
|
||||
src/sched_thread.h
|
||||
src/server_id_func.c
|
||||
src/server_id_func.h
|
||||
src/shared_buffer.c
|
||||
src/shared_buffer.h
|
||||
src/shared_func.c
|
||||
src/shared_func.h
|
||||
src/skiplist_common.h
|
||||
src/skiplist_set.c
|
||||
src/system_info.c
|
||||
src/system_info.h
|
||||
src/tests/test_allocator.c
|
||||
src/tests/test_atomic.c
|
||||
src/tests/test_blocked_queue.c
|
||||
src/tests/test_char_convert.c
|
||||
src/tests/test_char_convert_loader.c
|
||||
src/tests/test_crc32.c
|
||||
src/tests/test_data_visible.c
|
||||
src/tests/test_file_lock.c
|
||||
src/tests/test_file_write_hole.c
|
||||
src/tests/test_id_generator.c
|
||||
src/tests/test_ini_parser.c
|
||||
src/tests/test_json_parser.c
|
||||
src/tests/test_logger.c
|
||||
src/tests/test_mblock.c
|
||||
src/tests/test_multi_skiplist.c
|
||||
src/tests/test_mutex_lock_perf.c
|
||||
src/tests/test_normalize_path.c
|
||||
src/tests/test_pipe.c
|
||||
src/tests/test_pthread_lock.c
|
||||
src/tests/test_pthread_wait.c
|
||||
src/tests/test_queue_perf.c
|
||||
src/tests/test_sched_thread.c
|
||||
src/tests/test_server_id_func.c
|
||||
src/tests/test_skiplist.c
|
||||
src/tests/test_skiplist_set.c
|
||||
src/tests/test_split_string.c
|
||||
src/tests/test_thourands_seperator.c
|
||||
src/tests/test_thread_pool.c
|
||||
src/tests/test_uniq_skiplist.c
|
||||
src/thread_pool.c
|
||||
src/thread_pool.h
|
||||
src/uniq_skiplist.c
|
||||
src/uniq_skiplist.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: .gitignore
|
||||
HISTORY
|
||||
INSTALL
|
||||
debian/README.Debian
|
||||
debian/compat
|
||||
debian/control
|
||||
debian/libfastcommon-dev.install
|
||||
debian/libfastcommon.install
|
||||
debian/rules
|
||||
debian/source/format
|
||||
debian/watch
|
||||
doc/id_generator-Chinese.md
|
||||
doc/ini_file_reader-Chinese.md
|
||||
doc/php_log_file_performance-Chinese.md
|
||||
libfastcommon.spec
|
||||
make.sh
|
||||
php-fastcommon/config.m4
|
||||
php-fastcommon/fastcommon.ini
|
||||
php-fastcommon/php-fastcommon.spec.in
|
||||
src/Makefile.in
|
||||
src/fast_link_library.sh
|
||||
src/fc_list.h
|
||||
src/io_opt.c
|
||||
src/io_opt.h
|
||||
src/locked_list.h
|
||||
src/md5.c
|
||||
src/md5.h
|
||||
src/tests/Makefile
|
||||
src/tests/servers.conf
|
||||
src/tests/test.ini
|
||||
Copyright: __NO_COPYRIGHT_NOR_LICENSE__
|
||||
License: __NO_COPYRIGHT_NOR_LICENSE__
|
||||
|
||||
Files: src/skiplist.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
skiplist.h, support stable sort :)
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/skiplist_set.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
a set implemented by skiplist, the entry can occur only once
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/sockopt.c
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
socketopt.c
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/sockopt.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
socketopt.h
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/http_func.c
|
||||
Copyright: 2008 Happy Fish / YuQing
|
||||
2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
FastDFS may be copied only under the terms of the GNU General
|
||||
Public License V3, which may be found in the FastDFS source kit.
|
||||
Please visit the FastDFS Home Page http://www.fastken.com/ for more detail.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/id_generator.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
64 bits id generator for multi processes, the generated id format:
|
||||
32 bits timestamp + X bits machine id + Y bits of extra data + Z bits serial number
|
||||
such as 12 bits machine id, 0 bits extra data and 20 bits serial number
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/php7_ext_wrapper.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
php7 extension wrapper
|
||||
for compatibility, these wrapper functions are designed for old php version.
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/base64.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
base64.h
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: src/logger.h
|
||||
Copyright: 2020 YuQing <384681@qq.com>
|
||||
License: GPL-3.0+
|
||||
This program is free software: you can use, redistribute, and/or modify
|
||||
it under the terms of the Lesser GNU General Public License, version 3
|
||||
or later ("LGPL"), as published by the Free Software Foundation.
|
||||
.
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE.
|
||||
.
|
||||
You should have received a copy of the Lesser GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
.
|
||||
logger.h
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General Public License
|
||||
Version 3 can be found in `/usr/share/common-licenses/GPL-3'.
|
||||
|
||||
Files: README
|
||||
Copyright: 2010 Happy Fish / YuQing
|
||||
License: __UNKNOWN__
|
||||
libfastcommon may be copied only under the terms of the Less GNU General
|
||||
Public License(LGPL).
|
||||
Please visit the libfastcommon Home Page for more detail.
|
||||
English language: https://github.com/happyfish100/libfastcommon
|
||||
Chinese language: http://www.fastken.com/
|
||||
.
|
||||
c common functions library extracted from my open source projects FastDFS and
|
||||
FastDHT. this library is very simple and stable.
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# xml and html files (skipped):
|
||||
# php-fastcommon/test_file_put_contents.php
|
||||
# php-fastcommon/test.php
|
||||
# php-fastcommon/test_error_log.php
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Files marked as NO_LICENSE_TEXT_FOUND may be covered by the following
|
||||
# license/copyright files.
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# License file: LICENSE
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
.
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
.
|
||||
.
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
.
|
||||
0. Additional Definitions.
|
||||
.
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
.
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
.
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
.
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
.
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
.
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
.
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
.
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
.
|
||||
2. Conveying Modified Versions.
|
||||
.
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
.
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
.
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
.
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
.
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
.
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
.
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
.
|
||||
4. Combined Works.
|
||||
.
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
.
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
.
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
.
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
.
|
||||
d) Do one of the following:
|
||||
.
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
.
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
.
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
.
|
||||
5. Combined Libraries.
|
||||
.
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
.
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
.
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
.
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
.
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
.
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
.
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
|
|
@ -0,0 +1 @@
|
|||
usr/include/fastcommon/*
|
||||
|
|
@ -0,0 +1 @@
|
|||
usr/lib/libfastcommon.so*
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#!/usr/bin/make -f
|
||||
|
||||
export DESTDIR = $(CURDIR)/debian/tmp
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
|
||||
override_dh_auto_build:
|
||||
./make.sh clean && ./make.sh
|
||||
|
||||
override_dh_auto_install:
|
||||
./make.sh install
|
||||
dh_auto_install
|
||||
|
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
version=3
|
||||
opts="mode=git" https://github.com/happyfish100/libfastcommon.git \
|
||||
refs/tags/v([\d\.]+) debian uupdate
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
%define LibFastcommonDevel libfastcommon-devel
|
||||
%define LibFastcommonDebuginfo libfastcommon-debuginfo
|
||||
%define CommitVersion %(echo $COMMIT_VERSION)
|
||||
|
||||
Name: libfastcommon
|
||||
Version: 1.0.41
|
||||
Version: 1.0.83
|
||||
Release: 1%{?dist}
|
||||
Summary: c common functions library extracted from my open source projects FastDFS
|
||||
License: LGPL
|
||||
|
|
@ -12,21 +11,30 @@ Group: Arch/Tech
|
|||
URL: http://github.com/happyfish100/libfastcommon/
|
||||
Source: http://github.com/happyfish100/libfastcommon/%{name}-%{version}.tar.gz
|
||||
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
|
||||
|
||||
#Requires: /sbin/chkconfig
|
||||
#BuildRequires: perl %{_includedir}/linux/if.h gettext
|
||||
BuildRequires: libcurl-devel
|
||||
Requires: libcurl
|
||||
Requires: %__cp %__mv %__chmod %__grep %__mkdir %__install %__id
|
||||
|
||||
%define kernel_major %(uname -r | cut -d'.' -f1)
|
||||
%define kernel_minor %(uname -r | cut -d'.' -f2)
|
||||
%define kernel_ver_int %(expr %{kernel_major} \\* 100 + %{kernel_minor})
|
||||
%if %{kernel_ver_int} >= 514
|
||||
BuildRequires: liburing-devel >= 2.4
|
||||
Requires: liburing >= 2.4
|
||||
%endif
|
||||
|
||||
%description
|
||||
c common functions library extracted from my open source projects FastDFS.
|
||||
this library is very simple and stable. functions including: string, logger,
|
||||
chain, hash, socket, ini file reader, base64 encode / decode,
|
||||
url encode / decode, fasttimer etc.
|
||||
url encode / decode, fasttimer etc.
|
||||
commit version: %{CommitVersion}
|
||||
|
||||
%package devel
|
||||
Summary: Development header file
|
||||
Requires: libcurl-devel
|
||||
Requires: %{name}%{?_isa} = %{version}-%{release}
|
||||
|
||||
%description devel
|
||||
|
|
@ -56,12 +64,11 @@ rm -rf %{buildroot}
|
|||
%files
|
||||
%defattr(-,root,root,-)
|
||||
/usr/lib64/libfastcommon.so*
|
||||
/usr/lib/libfastcommon.so*
|
||||
|
||||
%files devel
|
||||
%defattr(-,root,root,-)
|
||||
/usr/include/fastcommon/*
|
||||
|
||||
%changelog
|
||||
* Mon Jun 23 2014 Zaixue Liao <liaozaixue@yongche.com>
|
||||
* Mon Jun 23 2014 Zaixue Liao
|
||||
- first RPM release (1.0)
|
||||
|
|
|
|||
157
make.sh
157
make.sh
|
|
@ -1,3 +1,9 @@
|
|||
GCC_VERSION=$(gcc -dM -E - < /dev/null | grep -w __GNUC__ | awk '{print $NF;}')
|
||||
if [ -z "$GCC_VERSION" ]; then
|
||||
echo -e "gcc not found, please install gcc first\n" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
tmp_src_filename=fast_check_bits.c
|
||||
cat <<EOF > $tmp_src_filename
|
||||
#include <stdio.h>
|
||||
|
|
@ -7,75 +13,132 @@ int main()
|
|||
{
|
||||
printf("%d\n", (int)sizeof(void*));
|
||||
printf("%d\n", (int)sizeof(off_t));
|
||||
#ifdef __GLIBC_MINOR__
|
||||
printf("%d\n", __GLIBC_MINOR__);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
gcc -D_FILE_OFFSET_BITS=64 -o a.out $tmp_src_filename
|
||||
output=`./a.out`
|
||||
output=$(./a.out)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "Can't find a.out program\n" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ -f /bin/expr ]; then
|
||||
EXPR=/bin/expr
|
||||
else
|
||||
elif [ -f /usr/bin/expr ]; then
|
||||
EXPR=/usr/bin/expr
|
||||
else
|
||||
EXPR=$(which expr)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo -e "Can't find expr program\n" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
count=0
|
||||
int_bytes=4
|
||||
off_bytes=8
|
||||
glibc_minor=0
|
||||
LIB_VERSION=lib64
|
||||
|
||||
for col in $output; do
|
||||
if [ $count -eq 0 ]; then
|
||||
int_bytes=$col
|
||||
else
|
||||
elif [ $count -eq 1 ]; then
|
||||
off_bytes=$col
|
||||
else
|
||||
glibc_minor=$col
|
||||
fi
|
||||
|
||||
count=`$EXPR $count + 1`
|
||||
count=$($EXPR $count + 1)
|
||||
done
|
||||
|
||||
/bin/rm -f a.out $tmp_src_filename
|
||||
|
||||
uname=$(uname)
|
||||
TARGET_PREFIX=$DESTDIR/usr
|
||||
if [ "$int_bytes" -eq 8 ]; then
|
||||
OS_BITS=64
|
||||
LIB_VERSION=lib64
|
||||
OS_BITS=64
|
||||
if [ $uname = 'Linux' ]; then
|
||||
osname=$(cat /etc/os-release | grep -w NAME | awk -F '=' '{print $2;}' | \
|
||||
awk -F '"' '{if (NF==3) {print $2} else {print $1}}' | awk '{print $1}')
|
||||
if [ $osname = 'Ubuntu' -o $osname = 'Debian' ]; then
|
||||
LIB_VERSION=lib
|
||||
else
|
||||
LIB_VERSION=lib64
|
||||
fi
|
||||
else
|
||||
LIB_VERSION=lib
|
||||
fi
|
||||
else
|
||||
OS_BITS=32
|
||||
LIB_VERSION=lib
|
||||
OS_BITS=32
|
||||
LIB_VERSION=lib
|
||||
fi
|
||||
|
||||
if [ "$off_bytes" -eq 8 ]; then
|
||||
OFF_BITS=64
|
||||
OFF_BITS=64
|
||||
else
|
||||
OFF_BITS=32
|
||||
OFF_BITS=32
|
||||
fi
|
||||
|
||||
DEBUG_FLAG=0
|
||||
|
||||
CFLAGS='-Wall -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE'
|
||||
export CC=gcc
|
||||
CFLAGS='-Wall'
|
||||
if [ -n "$GCC_VERSION" ] && [ $GCC_VERSION -ge 7 ]; then
|
||||
CFLAGS="$CFLAGS -Wformat-truncation=0 -Wformat-overflow=0"
|
||||
fi
|
||||
CFLAGS="$CFLAGS -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE"
|
||||
if [ "$DEBUG_FLAG" = "1" ]; then
|
||||
CFLAGS="$CFLAGS -g -DDEBUG_FLAG"
|
||||
else
|
||||
CFLAGS="$CFLAGS -g -O3"
|
||||
fi
|
||||
|
||||
INCS=''
|
||||
LIBS='-lm -ldl'
|
||||
if [ -f /usr/include/curl/curl.h ] || [ -f /usr/local/include/curl/curl.h ]; then
|
||||
CFLAGS="$CFLAGS -DUSE_LIBCURL"
|
||||
LIBS="$LIBS -lcurl"
|
||||
fi
|
||||
|
||||
uname=`uname`
|
||||
|
||||
HAVE_VMMETER_H=0
|
||||
HAVE_USER_H=0
|
||||
if [ "$uname" = "Linux" ]; then
|
||||
OS_NAME=OS_LINUX
|
||||
IOEVENT_USE=IOEVENT_USE_EPOLL
|
||||
|
||||
major_version=$(uname -r | awk -F . '{print $1;}')
|
||||
minor_version=$(uname -r | awk -F . '{print $2;}')
|
||||
if [ $major_version -eq 5 -a $minor_version -ge 14 ] || [ $major_version -gt 5 ]; then
|
||||
out=$(grep -F IORING_OP_SEND_ZC /usr/include/liburing/io_uring.h 2>/dev/null)
|
||||
if [ -n "$out" ]; then
|
||||
IOEVENT_USE=IOEVENT_USE_URING
|
||||
LIBS="$LIBS -luring"
|
||||
else
|
||||
IOEVENT_USE=IOEVENT_USE_EPOLL
|
||||
fi
|
||||
else
|
||||
IOEVENT_USE=IOEVENT_USE_EPOLL
|
||||
fi
|
||||
|
||||
if [ $glibc_minor -lt 17 ]; then
|
||||
LIBS="$LIBS -lrt"
|
||||
fi
|
||||
elif [ "$uname" = "FreeBSD" ] || [ "$uname" = "Darwin" ]; then
|
||||
OS_NAME=OS_FREEBSD
|
||||
IOEVENT_USE=IOEVENT_USE_KQUEUE
|
||||
if [ "$uname" = "Darwin" ]; then
|
||||
CFLAGS="$CFLAGS -DDARWIN"
|
||||
TARGET_PREFIX=$TARGET_PREFIX/local
|
||||
LIB_VERSION=lib
|
||||
else
|
||||
INCS="$INCS -I/usr/local/include"
|
||||
LIBS="$LIBS -L/usr/local/lib"
|
||||
fi
|
||||
|
||||
if [ -f /usr/include/sys/vmmeter.h ]; then
|
||||
|
|
@ -90,12 +153,10 @@ elif [ "$uname" = "SunOS" ]; then
|
|||
IOEVENT_USE=IOEVENT_USE_PORT
|
||||
CFLAGS="$CFLAGS -D_THREAD_SAFE"
|
||||
LIBS="$LIBS -lsocket -lnsl -lresolv"
|
||||
export CC=gcc
|
||||
elif [ "$uname" = "AIX" ]; then
|
||||
OS_NAME=OS_AIX
|
||||
IOEVENT_USE=IOEVENT_USE_NONE
|
||||
CFLAGS="$CFLAGS -D_THREAD_SAFE"
|
||||
export CC=gcc
|
||||
elif [ "$uname" = "HP-UX" ]; then
|
||||
OS_NAME=OS_HPUX
|
||||
IOEVENT_USE=IOEVENT_USE_NONE
|
||||
|
|
@ -104,6 +165,36 @@ else
|
|||
IOEVENT_USE=IOEVENT_USE_NONE
|
||||
fi
|
||||
|
||||
check_dirent_field()
|
||||
{
|
||||
field_name=$1
|
||||
upper_fname=$(echo $field_name | tr '[a-z]' '[A-Z]')
|
||||
|
||||
tmp_src_filename=fast_check_dirent.c
|
||||
cat <<EOF > $tmp_src_filename
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct dirent dir;
|
||||
dir.$field_name = 1;
|
||||
printf("#define HAVE_DIRENT_$upper_fname %d\n", dir.$field_name);
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
gcc -o a.out $tmp_src_filename 2>/dev/null && ./a.out
|
||||
/bin/rm -f a.out $tmp_src_filename
|
||||
|
||||
}
|
||||
|
||||
tmp_filename=fast_dirent_macros.txt
|
||||
check_dirent_field d_type > $tmp_filename
|
||||
check_dirent_field d_reclen >> $tmp_filename
|
||||
check_dirent_field d_namlen >> $tmp_filename
|
||||
check_dirent_field d_off >> $tmp_filename
|
||||
|
||||
cat <<EOF > src/_os_define.h
|
||||
#ifndef _OS_DEFINE_H
|
||||
#define _OS_DEFINE_H
|
||||
|
|
@ -126,13 +217,35 @@ cat <<EOF > src/_os_define.h
|
|||
#ifndef HAVE_USER_H
|
||||
#define HAVE_USER_H $HAVE_USER_H
|
||||
#endif
|
||||
|
||||
$(cat $tmp_filename && /bin/rm -f $tmp_filename)
|
||||
|
||||
#endif
|
||||
EOF
|
||||
|
||||
if [ -f /usr/lib/libpthread.so ] || [ -f /usr/local/lib/libpthread.so ] || [ -f /usr/lib64/libpthread.so ] || [ -f /usr/lib/libpthread.a ] || [ -f /usr/local/lib/libpthread.a ] || [ -f /usr/lib64/libpthread.a ]; then
|
||||
pthread_path=''
|
||||
for path in /lib /lib64 /usr/lib /usr/lib64 /usr/local/lib; do
|
||||
if [ -d $path ]; then
|
||||
pthread_path=$(find $path -name libpthread.so | head -n 1)
|
||||
if [ -n "$pthread_path" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
pthread_path=$(find $path -name libpthread.a | head -n 1)
|
||||
if [ -n "$pthread_path" ]; then
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$pthread_path" ]; then
|
||||
LIBS="$LIBS -lpthread"
|
||||
line=$(nm $pthread_path 2>/dev/null | grep -F pthread_rwlockattr_setkind_np | grep -w T)
|
||||
if [ -n "$line" ]; then
|
||||
CFLAGS="$CFLAGS -DWITH_PTHREAD_RWLOCKATTR_SETKIND_NP=1"
|
||||
fi
|
||||
elif [ -f /usr/lib/libc_r.so ]; then
|
||||
line=`nm -D /usr/lib/libc_r.so | grep pthread_create | grep -w T`
|
||||
line=$(nm -D /usr/lib/libc_r.so 2>/dev/null | grep -F pthread_create | grep -w T)
|
||||
if [ -n "$line" ]; then
|
||||
LIBS="$LIBS -lc_r"
|
||||
fi
|
||||
|
|
@ -151,7 +264,9 @@ sed_replace()
|
|||
|
||||
cd src
|
||||
cp Makefile.in Makefile
|
||||
sed_replace "s#\\\$(CC)#gcc#g" Makefile
|
||||
sed_replace "s#\\\$(CFLAGS)#$CFLAGS#g" Makefile
|
||||
sed_replace "s#\\\$(INCS)#$INCS#g" Makefile
|
||||
sed_replace "s#\\\$(LIBS)#$LIBS#g" Makefile
|
||||
sed_replace "s#\\\$(TARGET_PREFIX)#$TARGET_PREFIX#g" Makefile
|
||||
sed_replace "s#\\\$(LIB_VERSION)#$LIB_VERSION#g" Makefile
|
||||
|
|
@ -161,3 +276,11 @@ if [ "$1" = "clean" ]; then
|
|||
/bin/rm -f Makefile _os_define.h
|
||||
fi
|
||||
|
||||
cd tests
|
||||
cp Makefile.in Makefile
|
||||
sed_replace "s#\\\$(CC)#gcc#g" Makefile
|
||||
sed_replace "s#\\\$(INCS)#$INCS#g" Makefile
|
||||
sed_replace "s#\\\$(LIBS)#$LIBS#g" Makefile
|
||||
if [ "$1" = "clean" ]; then
|
||||
/bin/rm -f Makefile
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,3 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "php7_ext_wrapper.h"
|
||||
#include "ext/standard/info.h"
|
||||
#include "ext/standard/file.h"
|
||||
|
|
@ -79,26 +93,111 @@ const zend_fcall_info empty_fcall_info = { 0, NULL, NULL, NULL, NULL, 0, NULL, N
|
|||
{ NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args },
|
||||
#endif
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_version, 0, 0, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_gethostaddrs, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, if_alias_prefix)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_time33_hash, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, str)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_simple_hash, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, str)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_line_distance_km, 0, 0, 4)
|
||||
ZEND_ARG_INFO(0, lat1)
|
||||
ZEND_ARG_INFO(0, lon1)
|
||||
ZEND_ARG_INFO(0, lat2)
|
||||
ZEND_ARG_INFO(0, lon2)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_first_local_ip, 0, 0, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_next_local_ip, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, previous_ip)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_is_private_ip, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, ip)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_id_generator_init, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, filename)
|
||||
ZEND_ARG_INFO(0, machine_id)
|
||||
ZEND_ARG_INFO(0, mid_bits)
|
||||
ZEND_ARG_INFO(0, extra_bits)
|
||||
ZEND_ARG_INFO(0, sn_bits)
|
||||
ZEND_ARG_INFO(0, mode)
|
||||
ZEND_ARG_INFO(0, flags)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_id_generator_next, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, extra)
|
||||
ZEND_ARG_INFO(0, handle)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_id_generator_get_extra, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, id)
|
||||
ZEND_ARG_INFO(0, handle)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_id_generator_get_timestamp, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, id)
|
||||
ZEND_ARG_INFO(0, handle)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_id_generator_destroy, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, handle)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_ifconfigs, 0, 0, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_cpu_count, 0, 0, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_get_sysinfo, 0, 0, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_error_log, 0, 0, 1)
|
||||
ZEND_ARG_INFO(0, message)
|
||||
ZEND_ARG_INFO(0, message_type)
|
||||
ZEND_ARG_INFO(0, destination)
|
||||
ZEND_ARG_INFO(0, headers)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_file_put_contents, 0, 0, 2)
|
||||
ZEND_ARG_INFO(0, filename)
|
||||
ZEND_ARG_INFO(0, data)
|
||||
ZEND_ARG_INFO(0, flags)
|
||||
ZEND_ARG_INFO(0, context)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
// Every user visible function must have an entry in fastcommon_functions[].
|
||||
zend_function_entry fastcommon_functions[] = {
|
||||
ZEND_FE(fastcommon_version, NULL)
|
||||
ZEND_FE(fastcommon_gethostaddrs, NULL)
|
||||
ZEND_FE(fastcommon_time33_hash, NULL)
|
||||
ZEND_FE(fastcommon_simple_hash, NULL)
|
||||
ZEND_FE(fastcommon_get_line_distance_km, NULL)
|
||||
ZEND_FE(fastcommon_get_first_local_ip, NULL)
|
||||
ZEND_FE(fastcommon_get_next_local_ip, NULL)
|
||||
ZEND_FE(fastcommon_is_private_ip, NULL)
|
||||
ZEND_FE(fastcommon_id_generator_init, NULL)
|
||||
ZEND_FE(fastcommon_id_generator_next, NULL)
|
||||
ZEND_FE(fastcommon_id_generator_get_extra, NULL)
|
||||
ZEND_FE(fastcommon_id_generator_get_timestamp, NULL)
|
||||
ZEND_FE(fastcommon_id_generator_destroy, NULL)
|
||||
ZEND_FE(fastcommon_get_ifconfigs, NULL)
|
||||
ZEND_FE(fastcommon_get_cpu_count, NULL)
|
||||
ZEND_FE(fastcommon_get_sysinfo, NULL)
|
||||
ZEND_FE(fastcommon_error_log, NULL)
|
||||
ZEND_FE(fastcommon_file_put_contents, NULL)
|
||||
ZEND_FE(fastcommon_version, arginfo_version)
|
||||
ZEND_FE(fastcommon_gethostaddrs, arginfo_gethostaddrs)
|
||||
ZEND_FE(fastcommon_time33_hash, arginfo_time33_hash)
|
||||
ZEND_FE(fastcommon_simple_hash, arginfo_simple_hash)
|
||||
ZEND_FE(fastcommon_get_line_distance_km, arginfo_get_line_distance_km)
|
||||
ZEND_FE(fastcommon_get_first_local_ip, arginfo_get_first_local_ip)
|
||||
ZEND_FE(fastcommon_get_next_local_ip, arginfo_get_next_local_ip)
|
||||
ZEND_FE(fastcommon_is_private_ip, arginfo_is_private_ip)
|
||||
ZEND_FE(fastcommon_id_generator_init, arginfo_id_generator_init)
|
||||
ZEND_FE(fastcommon_id_generator_next, arginfo_id_generator_next)
|
||||
ZEND_FE(fastcommon_id_generator_get_extra, arginfo_id_generator_get_extra)
|
||||
ZEND_FE(fastcommon_id_generator_get_timestamp, arginfo_id_generator_get_timestamp)
|
||||
ZEND_FE(fastcommon_id_generator_destroy, arginfo_id_generator_destroy)
|
||||
ZEND_FE(fastcommon_get_ifconfigs, arginfo_get_ifconfigs)
|
||||
ZEND_FE(fastcommon_get_cpu_count, arginfo_get_cpu_count)
|
||||
ZEND_FE(fastcommon_get_sysinfo, arginfo_get_sysinfo)
|
||||
ZEND_FE(fastcommon_error_log, arginfo_error_log)
|
||||
ZEND_FE(fastcommon_file_put_contents, arginfo_file_put_contents)
|
||||
{NULL, NULL, NULL} /* Must be the last line */
|
||||
};
|
||||
|
||||
|
|
@ -153,7 +252,7 @@ PHP_MINIT_FUNCTION(fastcommon)
|
|||
log_try_init();
|
||||
le_consumer = zend_register_list_destructors_ex(id_generator_dtor, NULL,
|
||||
PHP_IDG_RESOURCE_NAME, module_number);
|
||||
if (hash_init(&idg_htable, simple_hash, 64, 0.75) != 0) {
|
||||
if (fc_hash_init(&idg_htable, fc_simple_hash, 64, 0.75) != 0) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +465,7 @@ ZEND_FUNCTION(fastcommon_simple_hash)
|
|||
RETURN_BOOL(false);
|
||||
}
|
||||
|
||||
RETURN_LONG(simple_hash(str, str_len) & 0x7FFFFFFF);
|
||||
RETURN_LONG(fc_simple_hash(str, str_len) & 0x7FFFFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -503,7 +602,7 @@ static struct idg_context *get_idg_context(const char *filename,
|
|||
key_info.extra_bits = extra_bits;
|
||||
key_info.sn_bits = sn_bits;
|
||||
|
||||
idg_context = (struct idg_context *)hash_find(&idg_htable,
|
||||
idg_context = (struct idg_context *)fc_hash_find(&idg_htable,
|
||||
&key_info, sizeof(key_info));
|
||||
if (idg_context == NULL) {
|
||||
idg_context = (struct idg_context *)malloc(sizeof(struct idg_context));
|
||||
|
|
@ -519,7 +618,7 @@ static struct idg_context *get_idg_context(const char *filename,
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
hash_insert_ex(&idg_htable, &key_info, sizeof(key_info),
|
||||
fc_hash_insert_ex(&idg_htable, &key_info, sizeof(key_info),
|
||||
idg_context, 0, false);
|
||||
}
|
||||
|
||||
|
|
@ -1138,8 +1237,8 @@ static PHPFileContext *fetch_file_context(const char *filename)
|
|||
|
||||
static int fc_open_file(PHPFileContext *ctx)
|
||||
{
|
||||
if ((ctx->fd = open(ctx->filename, O_WRONLY |
|
||||
O_CREAT | O_APPEND, 0644)) < 0)
|
||||
if ((ctx->fd = open(ctx->filename, O_WRONLY | O_CREAT |
|
||||
O_APPEND | O_CLOEXEC, 0644)) < 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"open file \"%s\" to write fail, "
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FASTCOMMON_H
|
||||
#define FASTCOMMON_H
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
.SUFFIXES: .c .o .lo
|
||||
|
||||
COMPILE = $(CC) $(CFLAGS)
|
||||
INC_PATH =
|
||||
INC_PATH = $(INCS)
|
||||
LIB_PATH = $(LIBS)
|
||||
TARGET_LIB = $(TARGET_PREFIX)/$(LIB_VERSION)
|
||||
|
||||
|
|
@ -9,36 +9,45 @@ FAST_SHARED_OBJS = hash.lo chain.lo shared_func.lo ini_file_reader.lo \
|
|||
logger.lo sockopt.lo base64.lo sched_thread.lo \
|
||||
http_func.lo md5.lo pthread_func.lo local_ip_func.lo \
|
||||
avl_tree.lo ioevent.lo ioevent_loop.lo fast_task_queue.lo \
|
||||
fast_timer.lo process_ctrl.lo fast_mblock.lo \
|
||||
fast_timer.lo locked_timer.lo process_ctrl.lo fast_mblock.lo \
|
||||
connection_pool.lo fast_mpool.lo fast_allocator.lo \
|
||||
fast_buffer.lo multi_skiplist.lo flat_skiplist.lo \
|
||||
system_info.lo fast_blocked_queue.lo id_generator.lo \
|
||||
char_converter.lo char_convert_loader.lo common_blocked_queue.lo \
|
||||
multi_socket_client.lo skiplist_set.lo json_parser.lo
|
||||
multi_socket_client.lo skiplist_set.lo uniq_skiplist.lo \
|
||||
json_parser.lo buffered_file_writer.lo server_id_func.lo \
|
||||
fc_queue.lo sorted_queue.lo fc_memory.lo shared_buffer.lo \
|
||||
thread_pool.lo array_allocator.lo sorted_array.lo spinlock.lo
|
||||
|
||||
FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \
|
||||
logger.o sockopt.o base64.o sched_thread.o \
|
||||
http_func.o md5.o pthread_func.o local_ip_func.o \
|
||||
avl_tree.o ioevent.o ioevent_loop.o fast_task_queue.o \
|
||||
fast_timer.o process_ctrl.o fast_mblock.o \
|
||||
fast_timer.o locked_timer.o process_ctrl.o fast_mblock.o \
|
||||
connection_pool.o fast_mpool.o fast_allocator.o \
|
||||
fast_buffer.o multi_skiplist.o flat_skiplist.o \
|
||||
system_info.o fast_blocked_queue.o id_generator.o \
|
||||
char_converter.o char_convert_loader.o common_blocked_queue.o \
|
||||
multi_socket_client.o skiplist_set.o json_parser.o
|
||||
multi_socket_client.o skiplist_set.o uniq_skiplist.o \
|
||||
json_parser.o buffered_file_writer.o server_id_func.o \
|
||||
fc_queue.o sorted_queue.o fc_memory.o shared_buffer.o \
|
||||
thread_pool.o array_allocator.o sorted_array.o spinlock.o
|
||||
|
||||
HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \
|
||||
shared_func.h pthread_func.h ini_file_reader.h _os_define.h \
|
||||
sockopt.h sched_thread.h http_func.h md5.h local_ip_func.h \
|
||||
avl_tree.h ioevent.h ioevent_loop.h fast_task_queue.h \
|
||||
fast_timer.h process_ctrl.h fast_mblock.h \
|
||||
fast_timer.h locked_timer.h process_ctrl.h fast_mblock.h \
|
||||
connection_pool.h fast_mpool.h fast_allocator.h \
|
||||
fast_buffer.h skiplist.h multi_skiplist.h flat_skiplist.h \
|
||||
skiplist_common.h system_info.h fast_blocked_queue.h \
|
||||
php7_ext_wrapper.h id_generator.h char_converter.h \
|
||||
char_convert_loader.h common_blocked_queue.h \
|
||||
multi_socket_client.h skiplist_set.h fc_list.h \
|
||||
json_parser.h
|
||||
multi_socket_client.h skiplist_set.h uniq_skiplist.h \
|
||||
fc_list.h locked_list.h json_parser.h buffered_file_writer.h \
|
||||
server_id_func.h fc_queue.h sorted_queue.h fc_memory.h \
|
||||
shared_buffer.h thread_pool.h fc_atomic.h array_allocator.h \
|
||||
sorted_array.h spinlock.h
|
||||
|
||||
ALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS)
|
||||
|
||||
|
|
@ -67,7 +76,9 @@ install:
|
|||
|
||||
install -m 755 $(SHARED_LIBS) $(TARGET_LIB)
|
||||
install -m 644 $(HEADER_FILES) $(TARGET_PREFIX)/include/fastcommon
|
||||
if [ ! -e $(TARGET_PREFIX)/lib/libfastcommon.so ]; then ln -s $(TARGET_LIB)/libfastcommon.so $(TARGET_PREFIX)/lib/libfastcommon.so; fi
|
||||
|
||||
@BUILDROOT=$$(echo "$(TARGET_PREFIX)" | grep BUILDROOT); \
|
||||
if [ -z "$$BUILDROOT" ] && [ "$(TARGET_LIB)" != "$(TARGET_PREFIX)/lib" ]; then ln -sf $(TARGET_LIB)/libfastcommon.so $(TARGET_PREFIX)/lib/libfastcommon.so; fi
|
||||
clean:
|
||||
rm -f $(ALL_OBJS) $(ALL_PRGS) $(ALL_LIBS)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "shared_func.h"
|
||||
#include "array_allocator.h"
|
||||
|
||||
int array_allocator_init_ex(ArrayAllocatorContext *ctx,
|
||||
const char *name_prefix, const int element_size,
|
||||
const int min_bits, const int max_bits,
|
||||
const bool need_lock)
|
||||
{
|
||||
const int obj_size = 0;
|
||||
const int reclaim_interval = 0;
|
||||
char name[32];
|
||||
struct fast_region_info regions[32];
|
||||
struct fast_region_info *region;
|
||||
int bit;
|
||||
int start;
|
||||
int end;
|
||||
int alloc_elements_once;
|
||||
int step;
|
||||
|
||||
ctx->element_size = element_size;
|
||||
ctx->min_count = (1 << min_bits);
|
||||
start = 0;
|
||||
alloc_elements_once = (1 << (max_bits - min_bits + 2));
|
||||
for (bit=min_bits, region=regions;
|
||||
bit<=max_bits; bit++, region++)
|
||||
{
|
||||
end = sizeof(VoidArray) + (1 << bit) * ctx->element_size;
|
||||
step = end - start;
|
||||
FAST_ALLOCATOR_INIT_REGION(*region, start,
|
||||
end, step, alloc_elements_once);
|
||||
alloc_elements_once /= 2;
|
||||
start = end;
|
||||
}
|
||||
|
||||
fc_combine_two_strings(name_prefix, "array", '-', name);
|
||||
return fast_allocator_init_ex(&ctx->allocator, name,
|
||||
obj_size, NULL, regions, region - regions, 0,
|
||||
0.9999, reclaim_interval, need_lock);
|
||||
}
|
||||
|
||||
VoidArray *array_allocator_alloc(ArrayAllocatorContext *ctx,
|
||||
const int target_count)
|
||||
{
|
||||
int alloc;
|
||||
int bytes;
|
||||
VoidArray *array;
|
||||
|
||||
if (target_count <= ctx->min_count) {
|
||||
alloc = ctx->min_count;
|
||||
} else if (is_power2(target_count)) {
|
||||
alloc = target_count;
|
||||
} else {
|
||||
alloc = ctx->min_count;
|
||||
while (alloc < target_count) {
|
||||
alloc *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
bytes = sizeof(VoidArray) + alloc * ctx->element_size;
|
||||
if ((array=fast_allocator_alloc(&ctx->allocator, bytes)) != NULL) {
|
||||
array->alloc = alloc;
|
||||
array->count = 0;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
VoidArray *array_allocator_realloc(ArrayAllocatorContext *ctx,
|
||||
VoidArray *old_array, const int target_count)
|
||||
{
|
||||
VoidArray *new_array;
|
||||
|
||||
if (old_array == NULL) {
|
||||
return array_allocator_alloc(ctx, target_count);
|
||||
}
|
||||
|
||||
if (old_array->alloc >= target_count) {
|
||||
return old_array;
|
||||
}
|
||||
|
||||
if ((new_array=array_allocator_alloc(ctx, target_count)) != NULL) {
|
||||
if (old_array->count > 0) {
|
||||
memcpy(new_array->elts, old_array->elts, ctx->
|
||||
element_size * old_array->count);
|
||||
}
|
||||
new_array->count = old_array->count;
|
||||
}
|
||||
|
||||
array_allocator_free(ctx, old_array);
|
||||
return new_array;
|
||||
}
|
||||
|
||||
int array_compare_element_int64(const int64_t *n1, const int64_t *n2)
|
||||
{
|
||||
int64_t sub;
|
||||
sub = *n1 - *n2;
|
||||
if (sub < 0) {
|
||||
return -1;
|
||||
} else if (sub > 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int array_compare_element_int32(const int32_t *n1, const int32_t *n2)
|
||||
{
|
||||
return *n1 - *n2;
|
||||
}
|
||||
|
||||
int array_compare_element_id_name(const id_name_pair_t *pair1,
|
||||
const id_name_pair_t *pair2)
|
||||
{
|
||||
return fc_compare_int64(pair1->id, pair2->id);
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3
|
||||
* or later ("AGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ARRAY_ALLOCATOR_H
|
||||
#define ARRAY_ALLOCATOR_H
|
||||
|
||||
#include "fast_allocator.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
char elts[0];
|
||||
} VoidArray;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
int64_t elts[0];
|
||||
} I64Array;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
int32_t elts[0];
|
||||
} I32Array;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
id_name_pair_t elts[0];
|
||||
} IdNameArray;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
void *elts[0];
|
||||
} PointerArray;
|
||||
|
||||
typedef struct array_allocator_context
|
||||
{
|
||||
struct fast_allocator_context allocator;
|
||||
int element_size;
|
||||
int min_count;
|
||||
} ArrayAllocatorContext;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int array_allocator_init_ex(ArrayAllocatorContext *ctx,
|
||||
const char *name_prefix, const int element_size,
|
||||
const int min_bits, const int max_bits,
|
||||
const bool need_lock);
|
||||
|
||||
VoidArray *array_allocator_alloc(ArrayAllocatorContext *ctx,
|
||||
const int target_count);
|
||||
|
||||
VoidArray *array_allocator_realloc(ArrayAllocatorContext *ctx,
|
||||
VoidArray *old_array, const int target_count);
|
||||
|
||||
static inline void array_allocator_free(ArrayAllocatorContext *ctx,
|
||||
VoidArray *array)
|
||||
{
|
||||
fast_allocator_free(&ctx->allocator, array);
|
||||
}
|
||||
|
||||
/* comparator for 64 bits integer */
|
||||
int array_compare_element_int64(const int64_t *n1, const int64_t *n2);
|
||||
|
||||
/* comparator for 32 bits integer */
|
||||
int array_compare_element_int32(const int32_t *n1, const int32_t *n2);
|
||||
|
||||
/* comparator for id name pair (sorted by id) */
|
||||
int array_compare_element_id_name(const id_name_pair_t *pair1,
|
||||
const id_name_pair_t *pair2);
|
||||
|
||||
#define array_allocator_init(ctx, name_prefix, \
|
||||
element_size, min_bits, max_bits) \
|
||||
array_allocator_init_ex(ctx, name_prefix, \
|
||||
element_size, min_bits, max_bits, true)
|
||||
|
||||
#define i64_array_allocator_init_ex(ctx, min_bits, max_bits, need_lock) \
|
||||
array_allocator_init_ex(ctx, "i64", sizeof(int64_t), \
|
||||
min_bits, max_bits, need_lock)
|
||||
|
||||
#define i64_array_allocator_init(ctx, min_bits, max_bits) \
|
||||
i64_array_allocator_init_ex(ctx, min_bits, max_bits, true)
|
||||
|
||||
#define i64_array_allocator_alloc(ctx, target_count) \
|
||||
(I64Array *)array_allocator_alloc(ctx, target_count)
|
||||
|
||||
#define i64_array_allocator_realloc(ctx, old_array, target_count) \
|
||||
(I64Array *)array_allocator_realloc(ctx, \
|
||||
(VoidArray *)old_array, target_count)
|
||||
|
||||
#define i64_array_allocator_free(ctx, array) \
|
||||
array_allocator_free(ctx, (VoidArray *)array)
|
||||
|
||||
|
||||
#define i32_array_allocator_init_ex(ctx, min_bits, max_bits, need_lock) \
|
||||
array_allocator_init_ex(ctx, "i32", sizeof(int32_t), \
|
||||
min_bits, max_bits, need_lock)
|
||||
|
||||
#define i32_array_allocator_init(ctx, min_bits, max_bits) \
|
||||
i32_array_allocator_init_ex(ctx, min_bits, max_bits, true)
|
||||
|
||||
#define i32_array_allocator_alloc(ctx, target_count) \
|
||||
(I32Array *)array_allocator_alloc(ctx, target_count)
|
||||
|
||||
#define i32_array_allocator_realloc(ctx, old_array, target_count) \
|
||||
(I32Array *)array_allocator_realloc(ctx, \
|
||||
(VoidArray *)old_array, target_count)
|
||||
|
||||
#define i32_array_allocator_free(ctx, array) \
|
||||
array_allocator_free(ctx, (VoidArray *)array)
|
||||
|
||||
|
||||
#define id_name_array_allocator_init_ex(ctx, min_bits, max_bits, need_lock) \
|
||||
array_allocator_init_ex(ctx, "id_name", sizeof(id_name_pair_t), \
|
||||
min_bits, max_bits, need_lock)
|
||||
|
||||
#define id_name_array_allocator_init(ctx, min_bits, max_bits) \
|
||||
id_name_array_allocator_init_ex(ctx, min_bits, max_bits, true)
|
||||
|
||||
#define id_name_array_allocator_alloc(ctx, target_count) \
|
||||
(IdNameArray *)array_allocator_alloc(ctx, target_count)
|
||||
|
||||
#define id_name_array_allocator_realloc(ctx, old_array, target_count) \
|
||||
(IdNameArray *)array_allocator_realloc(ctx, \
|
||||
(VoidArray *)old_array, target_count)
|
||||
|
||||
#define id_name_array_allocator_free(ctx, array) \
|
||||
array_allocator_free(ctx, (VoidArray *)array)
|
||||
|
||||
|
||||
#define ptr_array_allocator_init_ex(ctx, min_bits, max_bits, need_lock) \
|
||||
array_allocator_init_ex(ctx, "ptr", sizeof(void *), \
|
||||
min_bits, max_bits, need_lock)
|
||||
|
||||
#define ptr_array_allocator_init(ctx, min_bits, max_bits) \
|
||||
ptr_array_allocator_init_ex(ctx, min_bits, max_bits, true)
|
||||
|
||||
#define ptr_array_allocator_alloc(ctx, target_count) \
|
||||
(PointerArray *)array_allocator_alloc(ctx, target_count)
|
||||
|
||||
#define ptr_array_allocator_realloc(ctx, old_array, target_count) \
|
||||
(PointerArray *)array_allocator_realloc(ctx, \
|
||||
(VoidArray *)old_array, target_count)
|
||||
|
||||
#define ptr_array_allocator_free(ctx, array) \
|
||||
array_allocator_free(ctx, (VoidArray *)array)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "fc_memory.h"
|
||||
#include "avl_tree.h"
|
||||
|
||||
int avl_tree_init(AVLTreeInfo *tree, FreeDataFunc free_data_func, \
|
||||
|
|
@ -45,14 +61,9 @@ void avl_tree_destroy(AVLTreeInfo *tree)
|
|||
static AVLTreeNode *createTreeNode(AVLTreeNode *pParentNode, void *target_data)
|
||||
{
|
||||
AVLTreeNode *pNewNode;
|
||||
pNewNode = (AVLTreeNode *)malloc(sizeof(AVLTreeNode));
|
||||
pNewNode = (AVLTreeNode *)fc_malloc(sizeof(AVLTreeNode));
|
||||
if (pNewNode == NULL)
|
||||
{
|
||||
/*
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes fail!\n", __LINE__, \
|
||||
(int)sizeof(AVLTreeNode));
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef AVL_TREE_H
|
||||
#define AVL_TREE_H
|
||||
|
|
|
|||
37
src/base64.c
37
src/base64.c
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -16,6 +23,8 @@
|
|||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include "fc_memory.h"
|
||||
#include "shared_func.h"
|
||||
#include "base64.h"
|
||||
|
||||
/**
|
||||
|
|
@ -45,15 +54,11 @@ void base64_set_line_length(struct base64_context *context, const int length)
|
|||
* Usually contains only a combination of chars \n and \r.
|
||||
* Could be any chars not in set A-Z a-z 0-9 + /.
|
||||
*/
|
||||
void base64_set_line_separator(struct base64_context *context, \
|
||||
void base64_set_line_separator(struct base64_context *context,
|
||||
const char *pLineSeparator)
|
||||
{
|
||||
context->line_sep_len = snprintf(context->line_separator, \
|
||||
sizeof(context->line_separator), "%s", pLineSeparator);
|
||||
if (context->line_sep_len >= sizeof(context->line_separator))
|
||||
{
|
||||
context->line_sep_len = sizeof(context->line_separator) - 1;
|
||||
}
|
||||
context->line_sep_len = fc_safe_strcpy(context->
|
||||
line_separator, pLineSeparator);
|
||||
}
|
||||
|
||||
void base64_init_ex(struct base64_context *context, const int nLineLength, \
|
||||
|
|
@ -281,11 +286,9 @@ char *base64_decode_auto(struct base64_context *context, const char *src, \
|
|||
}
|
||||
else
|
||||
{
|
||||
pBuff = (char *)malloc(nNewLen);
|
||||
pBuff = (char *)fc_malloc(nNewLen);
|
||||
if (pBuff == NULL)
|
||||
{
|
||||
fprintf(stderr, "Can't malloc %d bytes\n", \
|
||||
nSrcLen + nPadLen + 1);
|
||||
*dest_len = 0;
|
||||
*dest = '\0';
|
||||
return dest;
|
||||
|
|
|
|||
21
src/base64.h
21
src/base64.h
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//base64.h
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include "shared_func.h"
|
||||
#include "logger.h"
|
||||
#include "fc_memory.h"
|
||||
#include "buffered_file_writer.h"
|
||||
|
||||
int buffered_file_writer_open_ex(BufferedFileWriter *writer,
|
||||
const char *filename, const int buffer_size,
|
||||
const int max_written_once, const int mode)
|
||||
{
|
||||
int result;
|
||||
int written_once;
|
||||
|
||||
writer->buffer_size = (buffer_size > 0) ? buffer_size : 64 * 1024;
|
||||
written_once = (max_written_once > 0) ? max_written_once : 256;
|
||||
if (written_once > writer->buffer_size)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"max_written_once: %d > buffer_size: %d",
|
||||
__LINE__, written_once, writer->buffer_size);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
writer->buff = (char *)fc_malloc(writer->buffer_size);
|
||||
if (writer->buff == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
fc_safe_strcpy(writer->filename, filename);
|
||||
writer->fd = open(writer->filename, O_WRONLY |
|
||||
O_CREAT | O_TRUNC | O_CLOEXEC, mode);
|
||||
if (writer->fd < 0)
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"open file %s fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, writer->filename,
|
||||
result, STRERROR(result));
|
||||
|
||||
free(writer->buff);
|
||||
writer->buff = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
writer->current = writer->buff;
|
||||
writer->buff_end = writer->buff + writer->buffer_size;
|
||||
writer->water_mark = writer->buff_end - written_once;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int buffered_file_writer_close(BufferedFileWriter *writer)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (writer->buff == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
result = buffered_file_writer_flush(writer);
|
||||
if (result == 0 && fsync(writer->fd) != 0)
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"fsync file %s fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, writer->filename,
|
||||
result, STRERROR(result));
|
||||
}
|
||||
|
||||
if (close(writer->fd) != 0)
|
||||
{
|
||||
if (result == 0)
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
}
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"close file %s fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, writer->filename,
|
||||
errno, STRERROR(errno));
|
||||
}
|
||||
|
||||
free(writer->buff);
|
||||
writer->buff = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int buffered_file_writer_flush(BufferedFileWriter *writer)
|
||||
{
|
||||
int result;
|
||||
int len;
|
||||
|
||||
len = writer->current - writer->buff;
|
||||
if (len == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fc_safe_write(writer->fd, writer->buff, len) != len)
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"write to file %s fail, "
|
||||
"errno: %d, error info: %s", __LINE__,
|
||||
writer->filename, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
writer->current = writer->buff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int buffered_file_writer_append(BufferedFileWriter *writer,
|
||||
const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
int remain_size;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
result = 0;
|
||||
for (i=0; i<2; i++)
|
||||
{
|
||||
remain_size = writer->buff_end - writer->current;
|
||||
va_start(ap, format);
|
||||
len = vsnprintf(writer->current, remain_size, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len < remain_size)
|
||||
{
|
||||
writer->current += len;
|
||||
if (writer->current > writer->water_mark)
|
||||
{
|
||||
result = buffered_file_writer_flush(writer);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (len > writer->buffer_size)
|
||||
{
|
||||
result = ENOSPC;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"too large output buffer, %d > %d!",
|
||||
__LINE__, len, writer->buffer_size);
|
||||
break;
|
||||
}
|
||||
|
||||
//maybe full, try again
|
||||
if ((result=buffered_file_writer_flush(writer)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int buffered_file_writer_append_buff(BufferedFileWriter *writer,
|
||||
const char *buff, const int len)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (len >= writer->water_mark - writer->current)
|
||||
{
|
||||
if ((result=buffered_file_writer_flush(writer)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (fc_safe_write(writer->fd, buff, len) != len)
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"write to file %s fail, "
|
||||
"errno: %d, error info: %s", __LINE__,
|
||||
writer->filename, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(writer->current, buff, len);
|
||||
writer->current += len;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BUFFERED_FILE_WRITER_H
|
||||
#define BUFFERED_FILE_WRITER_H
|
||||
|
||||
#include "common_define.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
int buffer_size;
|
||||
char filename[MAX_PATH_SIZE];
|
||||
char *buff;
|
||||
char *current;
|
||||
char *buff_end;
|
||||
char *water_mark;
|
||||
} BufferedFileWriter;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** open buffered file writer
|
||||
* parameters:
|
||||
* writer: the writer
|
||||
* filename: the filename to write
|
||||
* buffer_size: the buffer size, <= 0 for recommend 64KB
|
||||
* max_written_once: max written bytes per call, <= 0 for 256
|
||||
* mode: the file privilege such as 0644
|
||||
* return: error code, 0 for success, != 0 for errno
|
||||
*/
|
||||
int buffered_file_writer_open_ex(BufferedFileWriter *writer,
|
||||
const char *filename, const int buffer_size,
|
||||
const int max_written_once, const int mode);
|
||||
|
||||
static inline int buffered_file_writer_open(BufferedFileWriter *writer,
|
||||
const char *filename)
|
||||
{
|
||||
const int buffer_size = 0;
|
||||
const int max_written_once = 0;
|
||||
const int mode = 0644;
|
||||
return buffered_file_writer_open_ex(writer, filename,
|
||||
buffer_size, max_written_once, mode);
|
||||
}
|
||||
|
||||
/** close buffered file writer
|
||||
* parameters:
|
||||
* writer: the writer
|
||||
* return: error code, 0 for success, != 0 for errno
|
||||
*/
|
||||
int buffered_file_writer_close(BufferedFileWriter *writer);
|
||||
|
||||
int buffered_file_writer_append(BufferedFileWriter *writer,
|
||||
const char *format, ...);
|
||||
|
||||
int buffered_file_writer_append_buff(BufferedFileWriter *writer,
|
||||
const char *buff, const int len);
|
||||
|
||||
int buffered_file_writer_flush(BufferedFileWriter *writer);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
28
src/chain.c
28
src/chain.c
|
|
@ -1,16 +1,24 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "chain.h"
|
||||
#include "fc_memory.h"
|
||||
//#include "use_mmalloc.h"
|
||||
|
||||
void chain_init(ChainList *pList, const int type, FreeDataFunc freeDataFunc, \
|
||||
|
|
@ -69,7 +77,7 @@ int insertNodePrior(ChainList *pList, void *data)
|
|||
return EINVAL;
|
||||
}
|
||||
|
||||
pNode = (ChainNode *)malloc(sizeof(ChainNode));
|
||||
pNode = (ChainNode *)fc_malloc(sizeof(ChainNode));
|
||||
if (pNode == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
|
|
@ -95,7 +103,7 @@ int appendNode(ChainList *pList, void *data)
|
|||
return EINVAL;
|
||||
}
|
||||
|
||||
pNode = (ChainNode *)malloc(sizeof(ChainNode));
|
||||
pNode = (ChainNode *)fc_malloc(sizeof(ChainNode));
|
||||
if (pNode == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
|
|
@ -128,7 +136,7 @@ int insertNodeAsc(ChainList *pList, void *data)
|
|||
return EINVAL;
|
||||
}
|
||||
|
||||
pNew = (ChainNode *)malloc(sizeof(ChainNode));
|
||||
pNew = (ChainNode *)fc_malloc(sizeof(ChainNode));
|
||||
if (pNew == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
|
|
|
|||
21
src/chain.h
21
src/chain.h
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CHAIN_H
|
||||
#define CHAIN_H
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//char_convert_loader.h
|
||||
#ifndef CHAR_CONVERT_LOADER_H
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -20,7 +27,8 @@ int char_converter_init_ex(FastCharConverter *pCharConverter,
|
|||
const unsigned op)
|
||||
{
|
||||
int i;
|
||||
unsigned char src;
|
||||
unsigned char from;
|
||||
unsigned char to;
|
||||
if (count > FAST_MAX_CHAR_COUNT)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
|
|
@ -33,10 +41,15 @@ int char_converter_init_ex(FastCharConverter *pCharConverter,
|
|||
pCharConverter->count = count;
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
src = charPairs[i].src;
|
||||
pCharConverter->char_table[src].op = op;
|
||||
pCharConverter->char_table[src].dest = charPairs[i].dest;
|
||||
from = charPairs[i].src;
|
||||
to = charPairs[i].dest;
|
||||
pCharConverter->char_table[from].op = op;
|
||||
pCharConverter->char_table[from].dest = to;
|
||||
|
||||
pCharConverter->unescape_chars[to].op = op;
|
||||
pCharConverter->unescape_chars[to].dest = from;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -67,14 +80,14 @@ int std_spaces_add_backslash_converter_init(FastCharConverter *pCharConverter)
|
|||
#define SPACE_CHAR_PAIR_COUNT2 8
|
||||
FastCharPair pairs[SPACE_CHAR_PAIR_COUNT2];
|
||||
|
||||
pairs[0].src = '\0'; pairs[0].dest = '0';
|
||||
pairs[1].src = '\t'; pairs[1].dest = 't';
|
||||
pairs[2].src = '\n'; pairs[2].dest = 'n';
|
||||
pairs[3].src = '\v'; pairs[3].dest = 'v';
|
||||
pairs[4].src = '\f'; pairs[4].dest = 'f';
|
||||
pairs[5].src = '\r'; pairs[5].dest = 'r';
|
||||
pairs[6].src = ' '; pairs[6].dest = '-';
|
||||
pairs[7].src = '\\'; pairs[7].dest = '\\';
|
||||
FAST_CHAR_MAKE_PAIR(pairs[0], '\0', '0');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[1], '\t', 't');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[2], '\n', 'n');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[3], '\v', 'v');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[4], '\f', 'f');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[5], '\r', 'r');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[6], ' ', 's');
|
||||
FAST_CHAR_MAKE_PAIR(pairs[7], '\\', '\\');
|
||||
|
||||
return char_converter_init_ex(pCharConverter, pairs,
|
||||
SPACE_CHAR_PAIR_COUNT2, FAST_CHAR_OP_ADD_BACKSLASH);
|
||||
|
|
@ -142,7 +155,7 @@ int fast_char_convert(FastCharConverter *pCharConverter,
|
|||
out_size_sub1 = out_size - 1;
|
||||
for (; pi<end; pi++) {
|
||||
if (po - (unsigned char *)output >= out_size_sub1) {
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
logWarning("file: "__FILE__", line: %d, "
|
||||
"exceeds max size: %d", __LINE__, out_size);
|
||||
break;
|
||||
}
|
||||
|
|
@ -162,3 +175,47 @@ int fast_char_convert(FastCharConverter *pCharConverter,
|
|||
return count;
|
||||
}
|
||||
|
||||
int fast_char_unescape(FastCharConverter *pCharConverter, char *str, int *len)
|
||||
{
|
||||
int count;
|
||||
unsigned char *backslash;
|
||||
unsigned char *p;
|
||||
unsigned char *end;
|
||||
unsigned char *dest;
|
||||
|
||||
backslash = (unsigned char *)memchr(str, '\\', *len);
|
||||
if (backslash == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
end = (unsigned char *)str + *len;
|
||||
p = dest = backslash;
|
||||
while (p < end) {
|
||||
if (*p == '\\') {
|
||||
if (p + 1 < end) {
|
||||
if (pCharConverter->unescape_chars[p[1]].op ==
|
||||
FAST_CHAR_OP_ADD_BACKSLASH)
|
||||
{
|
||||
*dest++ = pCharConverter->unescape_chars[p[1]].dest;
|
||||
p += 2;
|
||||
++count;
|
||||
} else {
|
||||
*dest++ = *p++;
|
||||
}
|
||||
} else {
|
||||
*dest++ = *p++;
|
||||
}
|
||||
} else if (pCharConverter->unescape_chars[*p].op ==
|
||||
FAST_CHAR_OP_NO_BACKSLASH)
|
||||
{
|
||||
*dest++ = pCharConverter->unescape_chars[*p++].dest;
|
||||
++count;
|
||||
} else {
|
||||
*dest++ = *p++;
|
||||
}
|
||||
}
|
||||
|
||||
*len = dest - (unsigned char *)str;
|
||||
return count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//char_converter.h
|
||||
#ifndef CHAR_CONVERTER_H
|
||||
|
|
@ -24,6 +31,9 @@ extern "C" {
|
|||
#define FAST_CHAR_OP_ADD_BACKSLASH 1
|
||||
#define FAST_CHAR_OP_NO_BACKSLASH 2
|
||||
|
||||
#define FAST_CHAR_MAKE_PAIR(pair, from, to) \
|
||||
pair.src = from; pair.dest = to
|
||||
|
||||
typedef struct fast_char_pair
|
||||
{
|
||||
unsigned char src;
|
||||
|
|
@ -47,6 +57,11 @@ typedef struct fast_char_converter
|
|||
* char table to convert
|
||||
* */
|
||||
FastCharTarget char_table[FAST_MAX_CHAR_COUNT];
|
||||
|
||||
/*
|
||||
* char table to unescape
|
||||
* */
|
||||
FastCharTarget unescape_chars[FAST_MAX_CHAR_COUNT];
|
||||
} FastCharConverter;
|
||||
|
||||
/**
|
||||
|
|
@ -133,6 +148,21 @@ int fast_char_convert(FastCharConverter *pCharConverter,
|
|||
const char *input, const int input_len,
|
||||
char *output, int *out_len, const int out_size);
|
||||
|
||||
#define fast_char_escape(pCharConverter, input, input_len, \
|
||||
output, out_len, out_size) \
|
||||
fast_char_convert(pCharConverter, input, input_len, \
|
||||
output, out_len, out_size)
|
||||
|
||||
/**
|
||||
* char unescape function
|
||||
* parameters:
|
||||
* pCharConverter: the char converter
|
||||
* str: the string to unescape
|
||||
* len: the input string length and store the unscaped string length
|
||||
* return: converted char count
|
||||
*/
|
||||
int fast_char_unescape(FastCharConverter *pCharConverter, char *str, int *len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//common_blocked_queue.c
|
||||
|
||||
#include <errno.h>
|
||||
|
|
@ -11,29 +26,18 @@
|
|||
int common_blocked_queue_init_ex(struct common_blocked_queue *queue,
|
||||
const int alloc_elements_once)
|
||||
{
|
||||
const int64_t alloc_elements_limit = 0;
|
||||
int result;
|
||||
|
||||
if ((result=init_pthread_lock(&(queue->lock))) != 0)
|
||||
if ((result=init_pthread_lock_cond_pair(&queue->lc_pair)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"init_pthread_lock fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
result = pthread_cond_init(&(queue->cond), NULL);
|
||||
if (result != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"pthread_cond_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result=fast_mblock_init_ex(&queue->mblock,
|
||||
if ((result=fast_mblock_init_ex1(&queue->mblock, "queue-node",
|
||||
sizeof(struct common_blocked_node),
|
||||
alloc_elements_once, NULL, false)) != 0)
|
||||
alloc_elements_once, alloc_elements_limit,
|
||||
NULL, NULL, false)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
@ -46,17 +50,17 @@ int common_blocked_queue_init_ex(struct common_blocked_queue *queue,
|
|||
|
||||
void common_blocked_queue_destroy(struct common_blocked_queue *queue)
|
||||
{
|
||||
pthread_cond_destroy(&(queue->cond));
|
||||
pthread_mutex_destroy(&(queue->lock));
|
||||
destroy_pthread_lock_cond_pair(&queue->lc_pair);
|
||||
fast_mblock_destroy(&queue->mblock);
|
||||
}
|
||||
|
||||
int common_blocked_queue_push(struct common_blocked_queue *queue, void *data)
|
||||
int common_blocked_queue_push_ex(struct common_blocked_queue *queue,
|
||||
void *data, bool *notify)
|
||||
{
|
||||
int result;
|
||||
struct common_blocked_node *node;
|
||||
bool notify;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(queue->lock))) != 0)
|
||||
if ((result=pthread_mutex_lock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
|
|
@ -65,29 +69,29 @@ int common_blocked_queue_push(struct common_blocked_queue *queue, void *data)
|
|||
return result;
|
||||
}
|
||||
|
||||
node = (struct common_blocked_node *)fast_mblock_alloc_object(&queue->mblock);
|
||||
node = (struct common_blocked_node *)fast_mblock_alloc_object(
|
||||
&queue->mblock);
|
||||
if (node == NULL)
|
||||
{
|
||||
pthread_mutex_unlock(&(queue->lock));
|
||||
pthread_mutex_unlock(&(queue->lc_pair.lock));
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
node->data = data;
|
||||
node->next = NULL;
|
||||
|
||||
if (queue->tail == NULL)
|
||||
{
|
||||
queue->head = node;
|
||||
notify = true;
|
||||
*notify = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->tail->next = node;
|
||||
notify = false;
|
||||
*notify = false;
|
||||
}
|
||||
queue->tail = node;
|
||||
|
||||
if ((result=pthread_mutex_unlock(&(queue->lock))) != 0)
|
||||
if ((result=pthread_mutex_unlock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
|
|
@ -95,12 +99,55 @@ int common_blocked_queue_push(struct common_blocked_queue *queue, void *data)
|
|||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
if (notify)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void common_blocked_queue_push_chain_ex(struct common_blocked_queue *queue,
|
||||
struct common_blocked_chain *chain, bool *notify)
|
||||
{
|
||||
if (chain->head == NULL)
|
||||
{
|
||||
pthread_cond_signal(&(queue->cond));
|
||||
return;
|
||||
}
|
||||
|
||||
return 0;
|
||||
pthread_mutex_lock(&(queue->lc_pair.lock));
|
||||
if (queue->head == NULL)
|
||||
{
|
||||
queue->head = chain->head;
|
||||
*notify = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
queue->tail->next = chain->head;
|
||||
*notify = false;
|
||||
}
|
||||
queue->tail = chain->tail;
|
||||
pthread_mutex_unlock(&(queue->lc_pair.lock));
|
||||
}
|
||||
|
||||
void common_blocked_queue_return_nodes(struct common_blocked_queue *queue,
|
||||
struct common_blocked_node *node)
|
||||
{
|
||||
struct common_blocked_node *last;
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
last = node;
|
||||
while (last->next != NULL) {
|
||||
last = last->next;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&(queue->lc_pair.lock));
|
||||
last->next = queue->head;
|
||||
queue->head = node;
|
||||
if (queue->tail == NULL)
|
||||
{
|
||||
queue->tail = last;
|
||||
}
|
||||
pthread_mutex_unlock(&(queue->lc_pair.lock));
|
||||
}
|
||||
|
||||
void *common_blocked_queue_pop_ex(struct common_blocked_queue *queue,
|
||||
|
|
@ -110,7 +157,7 @@ void *common_blocked_queue_pop_ex(struct common_blocked_queue *queue,
|
|||
void *data;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(queue->lock))) != 0)
|
||||
if ((result=pthread_mutex_lock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
|
|
@ -129,7 +176,7 @@ void *common_blocked_queue_pop_ex(struct common_blocked_queue *queue,
|
|||
break;
|
||||
}
|
||||
|
||||
pthread_cond_wait(&(queue->cond), &(queue->lock));
|
||||
pthread_cond_wait(&(queue->lc_pair.cond), &(queue->lc_pair.lock));
|
||||
node = queue->head;
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +197,7 @@ void *common_blocked_queue_pop_ex(struct common_blocked_queue *queue,
|
|||
}
|
||||
} while (0);
|
||||
|
||||
if ((result=pthread_mutex_unlock(&(queue->lock))) != 0)
|
||||
if ((result=pthread_mutex_unlock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
|
|
@ -161,3 +208,104 @@ void *common_blocked_queue_pop_ex(struct common_blocked_queue *queue,
|
|||
return data;
|
||||
}
|
||||
|
||||
void *common_blocked_queue_timedpop(struct common_blocked_queue *queue,
|
||||
const int timeout, const int time_unit)
|
||||
{
|
||||
struct common_blocked_node *node;
|
||||
void *data;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutex_lock fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
do {
|
||||
node = queue->head;
|
||||
if (node == NULL)
|
||||
{
|
||||
fc_cond_timedwait(&queue->lc_pair, timeout, time_unit);
|
||||
node = queue->head;
|
||||
}
|
||||
|
||||
if (node != NULL)
|
||||
{
|
||||
queue->head = node->next;
|
||||
if (queue->head == NULL)
|
||||
{
|
||||
queue->tail = NULL;
|
||||
}
|
||||
|
||||
data = node->data;
|
||||
fast_mblock_free_object(&queue->mblock, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = NULL;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
if ((result=pthread_mutex_unlock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutex_unlock fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
struct common_blocked_node *common_blocked_queue_pop_all_nodes_ex(
|
||||
struct common_blocked_queue *queue, const bool blocked)
|
||||
{
|
||||
struct common_blocked_node *node;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutex_lock fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (queue->head == NULL)
|
||||
{
|
||||
if (blocked)
|
||||
{
|
||||
pthread_cond_wait(&(queue->lc_pair.cond), &(queue->lc_pair.lock));
|
||||
}
|
||||
}
|
||||
|
||||
node = queue->head;
|
||||
queue->head = queue->tail = NULL;
|
||||
if ((result=pthread_mutex_unlock(&(queue->lc_pair.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutex_unlock fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
void common_blocked_queue_free_all_nodes(struct common_blocked_queue *queue,
|
||||
struct common_blocked_node *node)
|
||||
{
|
||||
struct common_blocked_node *deleted;
|
||||
|
||||
pthread_mutex_lock(&(queue->lc_pair.lock));
|
||||
while (node != NULL) {
|
||||
deleted = node;
|
||||
node = node->next;
|
||||
fast_mblock_free_object(&queue->mblock, deleted);
|
||||
}
|
||||
pthread_mutex_unlock(&(queue->lc_pair.lock));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//common_blocked_queue.h
|
||||
|
||||
|
|
@ -24,13 +31,18 @@ struct common_blocked_node
|
|||
struct common_blocked_node *next;
|
||||
};
|
||||
|
||||
struct common_blocked_chain
|
||||
{
|
||||
struct common_blocked_node *head;
|
||||
struct common_blocked_node *tail;
|
||||
};
|
||||
|
||||
struct common_blocked_queue
|
||||
{
|
||||
struct common_blocked_node *head;
|
||||
struct common_blocked_node *tail;
|
||||
struct fast_mblock_man mblock;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
pthread_lock_cond_pair_t lc_pair;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -45,9 +57,10 @@ int common_blocked_queue_init_ex(struct common_blocked_queue *queue,
|
|||
|
||||
void common_blocked_queue_destroy(struct common_blocked_queue *queue);
|
||||
|
||||
static inline void common_blocked_queue_terminate(struct common_blocked_queue *queue)
|
||||
static inline void common_blocked_queue_terminate(
|
||||
struct common_blocked_queue *queue)
|
||||
{
|
||||
pthread_cond_signal(&(queue->cond));
|
||||
pthread_cond_signal(&(queue->lc_pair.cond));
|
||||
}
|
||||
|
||||
static inline void common_blocked_queue_terminate_all(
|
||||
|
|
@ -56,25 +69,136 @@ static inline void common_blocked_queue_terminate_all(
|
|||
int i;
|
||||
for (i=0; i<count; i++)
|
||||
{
|
||||
pthread_cond_signal(&(queue->cond));
|
||||
pthread_cond_signal(&(queue->lc_pair.cond));
|
||||
}
|
||||
}
|
||||
|
||||
int common_blocked_queue_push(struct common_blocked_queue *queue, void *data);
|
||||
//notify by the caller
|
||||
int common_blocked_queue_push_ex(struct common_blocked_queue *queue,
|
||||
void *data, bool *notify);
|
||||
|
||||
static inline int common_blocked_queue_push(struct common_blocked_queue
|
||||
*queue, void *data)
|
||||
{
|
||||
bool notify;
|
||||
int result;
|
||||
|
||||
if ((result=common_blocked_queue_push_ex(queue, data, ¬ify)) == 0)
|
||||
{
|
||||
if (notify)
|
||||
{
|
||||
pthread_cond_signal(&(queue->lc_pair.cond));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline struct common_blocked_node *common_blocked_queue_alloc_node(
|
||||
struct common_blocked_queue *queue)
|
||||
{
|
||||
struct common_blocked_node *node;
|
||||
|
||||
pthread_mutex_lock(&(queue->lc_pair.lock));
|
||||
node = (struct common_blocked_node *)fast_mblock_alloc_object(&queue->mblock);
|
||||
pthread_mutex_unlock(&(queue->lc_pair.lock));
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline void common_blocked_queue_free_node(
|
||||
struct common_blocked_queue *queue,
|
||||
struct common_blocked_node *node)
|
||||
{
|
||||
pthread_mutex_lock(&(queue->lc_pair.lock));
|
||||
fast_mblock_free_object(&queue->mblock, node);
|
||||
pthread_mutex_unlock(&(queue->lc_pair.lock));
|
||||
}
|
||||
|
||||
void common_blocked_queue_push_chain_ex(struct common_blocked_queue *queue,
|
||||
struct common_blocked_chain *chain, bool *notify);
|
||||
|
||||
static inline void common_blocked_queue_push_chain(
|
||||
struct common_blocked_queue *queue,
|
||||
struct common_blocked_chain *chain)
|
||||
{
|
||||
bool notify;
|
||||
|
||||
common_blocked_queue_push_chain_ex(queue, chain, ¬ify);
|
||||
if (notify)
|
||||
{
|
||||
pthread_cond_signal(&(queue->lc_pair.cond));
|
||||
}
|
||||
}
|
||||
|
||||
void common_blocked_queue_return_nodes(struct common_blocked_queue *queue,
|
||||
struct common_blocked_node *node);
|
||||
|
||||
void *common_blocked_queue_pop_ex(struct common_blocked_queue *queue,
|
||||
const bool blocked);
|
||||
|
||||
static inline void *common_blocked_queue_pop(struct common_blocked_queue *queue)
|
||||
static inline bool common_blocked_queue_empty(
|
||||
struct common_blocked_queue *queue)
|
||||
{
|
||||
return common_blocked_queue_pop_ex(queue, true);
|
||||
bool empty;
|
||||
|
||||
pthread_mutex_lock(&queue->lc_pair.lock);
|
||||
empty = (queue->head == NULL);
|
||||
pthread_mutex_unlock(&queue->lc_pair.lock);
|
||||
return empty;
|
||||
}
|
||||
|
||||
static inline void *common_blocked_queue_try_pop(struct common_blocked_queue *queue)
|
||||
static inline int common_blocked_queue_count(
|
||||
struct common_blocked_queue *queue)
|
||||
{
|
||||
return common_blocked_queue_pop_ex(queue, false);
|
||||
int count;
|
||||
struct common_blocked_node *node;
|
||||
|
||||
count = 0;
|
||||
pthread_mutex_lock(&queue->lc_pair.lock);
|
||||
node = queue->head;
|
||||
while (node != NULL)
|
||||
{
|
||||
++count;
|
||||
node = node->next;
|
||||
}
|
||||
pthread_mutex_unlock(&queue->lc_pair.lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
#define common_blocked_queue_pop(queue) \
|
||||
common_blocked_queue_pop_ex(queue, true)
|
||||
|
||||
#define common_blocked_queue_try_pop(queue) \
|
||||
common_blocked_queue_pop_ex(queue, false)
|
||||
|
||||
struct common_blocked_node *common_blocked_queue_pop_all_nodes_ex(
|
||||
struct common_blocked_queue *queue, const bool blocked);
|
||||
|
||||
#define common_blocked_queue_pop_all_nodes(queue) \
|
||||
common_blocked_queue_pop_all_nodes_ex(queue, true)
|
||||
|
||||
#define common_blocked_queue_try_pop_all_nodes(queue) \
|
||||
common_blocked_queue_pop_all_nodes_ex(queue, false)
|
||||
|
||||
#define common_blocked_queue_free_one_node(queue, node) \
|
||||
common_blocked_queue_free_node(&queue->mblock, node)
|
||||
|
||||
void common_blocked_queue_free_all_nodes(struct common_blocked_queue *queue,
|
||||
struct common_blocked_node *node);
|
||||
|
||||
void *common_blocked_queue_timedpop(struct common_blocked_queue *queue,
|
||||
const int timeout, const int time_unit);
|
||||
|
||||
#define common_blocked_queue_timedpop_sec(queue, timeout) \
|
||||
common_blocked_queue_timedpop(queue, timeout, FC_TIME_UNIT_SECOND)
|
||||
|
||||
#define common_blocked_queue_timedpop_ms(queue, timeout) \
|
||||
common_blocked_queue_timedpop(queue, timeout, FC_TIME_UNIT_MSECOND)
|
||||
|
||||
#define common_blocked_queue_timedpop_us(queue, timeout) \
|
||||
common_blocked_queue_timedpop(queue, timeout, FC_TIME_UNIT_USECOND)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//common_define.h
|
||||
|
||||
|
|
@ -14,6 +21,7 @@
|
|||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
|
|
@ -27,6 +35,7 @@ typedef DWORD (WINAPI *ThreadEntranceFunc)(LPVOID lpThreadParameter);
|
|||
#else
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <inttypes.h>
|
||||
|
|
@ -34,6 +43,14 @@ typedef DWORD (WINAPI *ThreadEntranceFunc)(LPVOID lpThreadParameter);
|
|||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifdef OS_FREEBSD
|
||||
//#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
/* Internet address (兼容IPv6长度). */
|
||||
typedef uint64_t in_addr_64_t;
|
||||
|
||||
#define FILE_SEPERATOR "/"
|
||||
typedef int SOCKET;
|
||||
#define closesocket close
|
||||
|
|
@ -51,6 +68,10 @@ extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
|
|||
|
||||
#include "_os_define.h"
|
||||
|
||||
#ifdef OS_LINUX
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
#ifndef PTHREAD_MUTEX_ERRORCHECK
|
||||
#define PTHREAD_MUTEX_ERRORCHECK PTHREAD_MUTEX_ERRORCHECK_NP
|
||||
|
|
@ -86,14 +107,42 @@ extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
|
|||
#define CONF_FILE_DIR "conf"
|
||||
#define DEFAULT_CONNECT_TIMEOUT 10
|
||||
#define DEFAULT_NETWORK_TIMEOUT 30
|
||||
#define DEFAULT_MAX_CONNECTONS 1024
|
||||
#define DEFAULT_MAX_CONNECTONS 256
|
||||
#define DEFAULT_WORK_THREADS 4
|
||||
#define SYNC_LOG_BUFF_DEF_INTERVAL 10
|
||||
#define TIME_NONE -1
|
||||
|
||||
#define IP_ADDRESS_SIZE 16
|
||||
#define FC_BYTES_ONE_KB ( 1 << 10)
|
||||
#define FC_BYTES_ONE_MB ( 1 << 20)
|
||||
#define FC_BYTES_ONE_GB ( 1 << 30)
|
||||
#define FC_BYTES_ONE_TB (1LL << 40)
|
||||
#define FC_BYTES_ONE_PB (1LL << 50)
|
||||
#define FC_BYTES_ONE_EB (1LL << 60)
|
||||
|
||||
#if defined(IOV_MAX) && IOV_MAX > 256
|
||||
#define FC_IOV_BATCH_SIZE 256
|
||||
#else
|
||||
#define FC_IOV_BATCH_SIZE IOV_MAX
|
||||
#endif
|
||||
|
||||
#define IPV4_ADDRESS_SIZE INET_ADDRSTRLEN //16
|
||||
#define IPV6_ADDRESS_SIZE INET6_ADDRSTRLEN //46
|
||||
#define IP_ADDRESS_SIZE INET6_ADDRSTRLEN //46
|
||||
#define FORMATTED_IP_SIZE (IP_ADDRESS_SIZE + 2)
|
||||
#define INFINITE_FILE_SIZE (256 * 1024LL * 1024 * 1024 * 1024 * 1024LL)
|
||||
|
||||
#define FILE_RESOURCE_TAG_STR "file://"
|
||||
#define FILE_RESOURCE_TAG_LEN (sizeof(FILE_RESOURCE_TAG_STR) - 1)
|
||||
|
||||
#define IS_FILE_RESOURCE(filename) \
|
||||
(strncasecmp(filename, FILE_RESOURCE_TAG_STR, \
|
||||
FILE_RESOURCE_TAG_LEN) == 0)
|
||||
|
||||
#define IS_FILE_RESOURCE_EX(filename) \
|
||||
((filename)->len >= FILE_RESOURCE_TAG_LEN && \
|
||||
memcmp((filename)->str, FILE_RESOURCE_TAG_STR, \
|
||||
FILE_RESOURCE_TAG_LEN) == 0)
|
||||
|
||||
#ifndef byte
|
||||
#define byte signed char
|
||||
#endif
|
||||
|
|
@ -112,16 +161,50 @@ extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
|
|||
#define ECANCELED 125
|
||||
#endif
|
||||
|
||||
#ifndef ENODATA
|
||||
#define ENODATA 61 /* No data available */
|
||||
#endif
|
||||
|
||||
#ifndef ENONET
|
||||
#define ENONET 64 /* Machine is not on the network */
|
||||
#endif
|
||||
|
||||
#define compile_barrier() __asm__ __volatile__("" : : : "memory")
|
||||
|
||||
#define IS_UPPER_HEX(ch) ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'F'))
|
||||
#define IS_HEX_CHAR(ch) (IS_UPPER_HEX(ch) || (ch >= 'a' && ch <= 'f'))
|
||||
#define FC_IS_DIGITAL(ch) (ch >= '0' && ch <= '9')
|
||||
#define FC_IS_LETTER(ch) ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
|
||||
#define FC_IS_UPPER_LETTER(ch) (ch >= 'A' && ch <= 'Z')
|
||||
#define FC_IS_LOWER_LETTER(ch) (ch >= 'a' && ch <= 'z')
|
||||
#define FC_MIN(v1, v2) ((v1) < (v2) ? (v1) : (v2))
|
||||
#define FC_MAX(v1, v2) ((v1) > (v2) ? (v1) : (v2))
|
||||
#define FC_ABS(n) ((n) >= 0 ? (n) : -1 * (n))
|
||||
#define FC_NEGATIVE(n) ((n) <= 0 ? (n) : -1 * (n))
|
||||
|
||||
#define FC_TIME_UNIT_SECOND 's' //second
|
||||
#define FC_TIME_UNIT_MSECOND 'm' //millisecond
|
||||
#define FC_TIME_UNIT_USECOND 'u' //microsecond
|
||||
#define FC_TIME_UNIT_NSECOND 'n' //nanosecond
|
||||
|
||||
#define FC_1E01 10LL
|
||||
#define FC_1E02 100LL
|
||||
#define FC_1E03 1000LL
|
||||
#define FC_1E04 10000LL
|
||||
#define FC_1E05 100000LL
|
||||
#define FC_1E06 1000000LL
|
||||
#define FC_1E07 10000000LL
|
||||
#define FC_1E08 100000000LL
|
||||
#define FC_1E09 1000000000LL
|
||||
#define FC_1E10 10000000000LL
|
||||
#define FC_1E11 100000000000LL
|
||||
#define FC_1E12 1000000000000LL
|
||||
#define FC_1E13 10000000000000LL
|
||||
#define FC_1E14 100000000000000LL
|
||||
#define FC_1E15 1000000000000000LL
|
||||
#define FC_1E16 10000000000000000LL
|
||||
#define FC_1E17 100000000000000000LL
|
||||
#define FC_1E18 1000000000000000000LL
|
||||
|
||||
#define STRERROR(no) (strerror(no) != NULL ? strerror(no) : "Unkown error")
|
||||
|
||||
|
|
@ -132,10 +215,12 @@ extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
|
|||
#define st_ctimensec st_ctim.tv_nsec
|
||||
#endif
|
||||
#elif defined(OS_FREEBSD)
|
||||
#ifndef st_atimensec
|
||||
#define st_atimensec st_atimespec.tv_nsec
|
||||
#define st_mtimensec st_mtimespec.tv_nsec
|
||||
#define st_ctimensec st_ctimespec.tv_nsec
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -155,6 +240,12 @@ typedef struct
|
|||
char patch;
|
||||
} Version;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char **strs;
|
||||
int count;
|
||||
} str_ptr_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *key;
|
||||
|
|
@ -188,6 +279,12 @@ typedef struct
|
|||
int count;
|
||||
} string_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int64_t id;
|
||||
string_t name;
|
||||
} id_name_pair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
string_t key;
|
||||
|
|
@ -200,12 +297,80 @@ typedef struct
|
|||
int count;
|
||||
} key_value_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int32_t *elts;
|
||||
int count;
|
||||
} int32_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int64_t *elts;
|
||||
int count;
|
||||
} int64_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
id_name_pair_t *elts;
|
||||
int count;
|
||||
} id_name_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
void *elts;
|
||||
int count;
|
||||
} void_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
} pthread_lock_cond_pair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int count;
|
||||
struct iovec *iovs;
|
||||
} iovec_array_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char buff[PATH_MAX];
|
||||
string_t s;
|
||||
} FilenameString;
|
||||
|
||||
typedef struct {
|
||||
char *filename;
|
||||
char *tmp_filename;
|
||||
int fd;
|
||||
} SafeWriteFileInfo;
|
||||
|
||||
typedef struct {
|
||||
char filename[PATH_MAX];
|
||||
int fd;
|
||||
} FilenameFDPair;
|
||||
|
||||
typedef void (*FreeDataFunc)(void *ptr);
|
||||
typedef int (*CompareFunc)(void *p1, void *p2);
|
||||
typedef void* (*MallocFunc)(size_t size);
|
||||
|
||||
#define TO_UPPERCASE(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 32 : c)
|
||||
#define MEM_ALIGN(x) (((x) + 7) & (~7))
|
||||
|
||||
#define MEM_ALIGN_FLOOR(x, align_size) ((x) & (~(align_size - 1)))
|
||||
#define MEM_ALIGN_CEIL(x, align_size) \
|
||||
(((x) + (align_size - 1)) & (~(align_size - 1)))
|
||||
#define MEM_ALIGN(x) MEM_ALIGN_CEIL(x, 8)
|
||||
|
||||
#define MEM_ALIGN_FLOOR_BY_MASK(x, align_mask) ((x) & (~align_mask))
|
||||
#define MEM_ALIGN_CEIL_BY_MASK(x, align_mask) \
|
||||
(((x) + align_mask) & (~align_mask))
|
||||
|
||||
#define FC_INIT_CHAIN(chain) (chain).head = (chain).tail = NULL
|
||||
#define FC_IS_CHAIN_EMPTY(chain) ((chain).head == NULL)
|
||||
|
||||
#define FC_SET_CHAIN_TAIL_NEXT(chain, type, ptr) \
|
||||
((type *)(chain).tail)->next = ptr
|
||||
|
||||
#ifdef WIN32
|
||||
#define strcasecmp _stricmp
|
||||
|
|
@ -238,6 +403,12 @@ typedef void* (*MallocFunc)(size_t size);
|
|||
//for printf format %.*s
|
||||
#define FC_PRINTF_STAR_STRING_PARAMS(s) (s).len, (s).str
|
||||
|
||||
#define FC_SET_IOVEC(iovec, buff, len) \
|
||||
do { \
|
||||
(iovec).iov_base = buff; \
|
||||
(iovec).iov_len = len; \
|
||||
} while (0)
|
||||
|
||||
#define FC_SET_STRING(dest, src) \
|
||||
do { \
|
||||
(dest).str = src; \
|
||||
|
|
@ -250,6 +421,8 @@ typedef void* (*MallocFunc)(size_t size);
|
|||
(dest).len = l; \
|
||||
} while (0)
|
||||
|
||||
#define FC_SET_STRING_EMPTY(dest, s) FC_SET_STRING_EX(dest, s, 0)
|
||||
|
||||
#define FC_SET_STRING_NULL(dest) \
|
||||
do { \
|
||||
(dest).str = NULL; \
|
||||
|
|
@ -259,6 +432,13 @@ typedef void* (*MallocFunc)(size_t size);
|
|||
#define FC_IS_NULL_STRING(s) ((s)->str == NULL)
|
||||
#define FC_IS_EMPTY_STRING(s) ((s)->len == 0)
|
||||
|
||||
#define FC_INIT_FILENAME_STRING(filename) \
|
||||
FC_SET_STRING_EX((filename).s, (filename).buff, 0)
|
||||
|
||||
#define FC_FILENAME_STRING_OBJ(filename) ((filename).s)
|
||||
#define FC_FILENAME_STRING_PTR(filename) ((filename).buff)
|
||||
#define FC_FILENAME_BUFFER_SIZE(filename) sizeof((filename).buff)
|
||||
|
||||
#define fc_compare_string(s1, s2) fc_string_compare(s1, s2)
|
||||
|
||||
static inline int fc_string_compare(const string_t *s1, const string_t *s2)
|
||||
|
|
@ -275,6 +455,14 @@ static inline int fc_string_compare(const string_t *s1, const string_t *s2)
|
|||
}
|
||||
}
|
||||
|
||||
static inline bool fc_string_equal_ex(const char *str1,
|
||||
const int len1, const char *str2, const int len2)
|
||||
{
|
||||
return (len1 == len2) && (memcmp(str1, str2, len1) == 0);
|
||||
}
|
||||
#define fc_string_equals_ex(str1, len1, str2, len2) \
|
||||
fc_string_equal_ex(str1, len1, str2, len2)
|
||||
|
||||
static inline bool fc_string_equal(const string_t *s1, const string_t *s2)
|
||||
{
|
||||
return (s1->len == s2->len) && (memcmp(s1->str, s2->str, s1->len) == 0);
|
||||
|
|
@ -289,6 +477,88 @@ static inline bool fc_string_equal2(const string_t *s1,
|
|||
#define fc_string_equals(s1, s2) fc_string_equal(s1, s2)
|
||||
#define fc_string_equals2(s1, str2, len2) fc_string_equal2(s1, str2, len2)
|
||||
|
||||
|
||||
#define fc_case_compare_string(s1, s2) fc_string_case_compare(s1, s2)
|
||||
|
||||
static inline int fc_string_case_compare(const string_t *s1, const string_t *s2)
|
||||
{
|
||||
int result;
|
||||
if (s1->len == s2->len) {
|
||||
return strncasecmp(s1->str, s2->str, s1->len);
|
||||
} else if (s1->len < s2->len) {
|
||||
result = strncasecmp(s1->str, s2->str, s1->len);
|
||||
return result == 0 ? -1 : result;
|
||||
} else {
|
||||
result = strncasecmp(s1->str, s2->str, s2->len);
|
||||
return result == 0 ? 1 : result;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool fc_string_case_equal(const string_t *s1, const string_t *s2)
|
||||
{
|
||||
return (s1->len == s2->len) && (strncasecmp(s1->str, s2->str, s1->len) == 0);
|
||||
}
|
||||
|
||||
static inline bool fc_string_case_equal2(const string_t *s1,
|
||||
const char *str2, const int len2)
|
||||
{
|
||||
return (s1->len == len2) && (strncasecmp(s1->str, str2, s1->len) == 0);
|
||||
}
|
||||
|
||||
#define fc_string_case_equals(s1, s2) fc_string_case_equal(s1, s2)
|
||||
#define fc_string_case_equals2(s1, str2, len2) \
|
||||
fc_string_case_equal2(s1, str2, len2)
|
||||
|
||||
|
||||
static inline int fc_compare_int64(const int64_t n1, const int64_t n2)
|
||||
{
|
||||
int64_t sub;
|
||||
sub = n1 - n2;
|
||||
if (sub < 0) {
|
||||
return -1;
|
||||
} else if (sub > 0) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OS_LINUX
|
||||
static inline int fc_fallocate(int fd, const int64_t size)
|
||||
{
|
||||
if (fallocate(fd, 0, 0, size) < 0) {
|
||||
if (errno == EOPNOTSUPP) {
|
||||
return ftruncate(fd, size);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define fc_fallocate(fd, size) ftruncate(fd, size)
|
||||
#define posix_fadvise(fd, offset, len, advice) 0
|
||||
|
||||
#ifndef POSIX_FADV_NORMAL
|
||||
#define POSIX_FADV_NORMAL 0 /* No further special treatment. */
|
||||
#define POSIX_FADV_RANDOM 1 /* Expect random page references. */
|
||||
#define POSIX_FADV_SEQUENTIAL 2 /* Expect sequential page references. */
|
||||
#define POSIX_FADV_WILLNEED 3 /* Will need these pages. */
|
||||
#define POSIX_FADV_DONTNEED 4 /* Don't need these pages. */
|
||||
#define POSIX_FADV_NOREUSE 5 /* Data will be accessed once. */
|
||||
#endif
|
||||
|
||||
#ifdef OS_FREEBSD
|
||||
//#define fdatasync(fd) syscall(SYS_fdatasync, fd)
|
||||
#define fdatasync(fd) fsync(fd)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#define FC_MACRO_STRINGIFY(x) #x
|
||||
#define FC_MACRO_TOSTRING(x) FC_MACRO_STRINGIFY(x)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//connection_pool.h
|
||||
|
||||
|
|
@ -16,9 +23,11 @@
|
|||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "common_define.h"
|
||||
#include "pthread_func.h"
|
||||
#include "hash.h"
|
||||
#include "fast_mblock.h"
|
||||
#include "ini_file_reader.h"
|
||||
#include "pthread_func.h"
|
||||
#include "sockopt.h"
|
||||
#include "hash.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -28,14 +37,111 @@ extern "C" {
|
|||
(strcmp((conn).ip_addr, target_ip) == 0 && \
|
||||
(conn).port == target_port)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int sock;
|
||||
int port;
|
||||
char ip_addr[INET6_ADDRSTRLEN];
|
||||
int socket_domain; //socket domain, AF_INET, AF_INET6 or AF_UNSPEC for auto dedect
|
||||
#define FC_CONNECTION_SERVER_EQUAL1(conn1, conn2) \
|
||||
(strcmp((conn1).ip_addr, (conn2).ip_addr) == 0 && \
|
||||
(conn1).port == (conn2).port)
|
||||
|
||||
typedef enum {
|
||||
fc_comm_type_sock = 0,
|
||||
fc_comm_type_rdma,
|
||||
fc_comm_type_both
|
||||
} FCCommunicationType;
|
||||
|
||||
typedef struct {
|
||||
int sock;
|
||||
uint16_t port;
|
||||
uint8_t af; //address family, AF_INET, AF_INET6 or AF_UNSPEC for auto dedect
|
||||
bool shared; //for connection pool
|
||||
FCCommunicationType comm_type;
|
||||
bool validate_flag; //for connection pool
|
||||
char ip_addr[IP_ADDRESS_SIZE];
|
||||
void *arg1; //for RDMA
|
||||
char args[0]; //for extra data
|
||||
} ConnectionInfo;
|
||||
|
||||
struct fc_server_config;
|
||||
struct ibv_pd;
|
||||
typedef void (*fc_set_busy_polling_callback)(const bool busy_polling);
|
||||
typedef struct ibv_pd *(*fc_alloc_pd_callback)(const char **ip_addrs,
|
||||
const int count, const int port);
|
||||
typedef int (*fc_get_connection_size_callback)();
|
||||
typedef int (*fc_init_connection_callback)(ConnectionInfo *conn,
|
||||
const bool double_buffers, const int buffer_size, void *arg);
|
||||
typedef int (*fc_make_connection_callback)(ConnectionInfo *conn,
|
||||
const char *service_name, const int timeout_ms,
|
||||
const char *bind_ipaddr, const bool log_connect_error);
|
||||
typedef bool (*fc_is_connected_callback)(ConnectionInfo *conn);
|
||||
typedef bool (*fc_send_done_callback)(ConnectionInfo *conn);
|
||||
typedef void (*fc_close_connection_callback)(ConnectionInfo *conn);
|
||||
typedef void (*fc_destroy_connection_callback)(ConnectionInfo *conn);
|
||||
|
||||
typedef BufferInfo *(*fc_rdma_get_recv_buffer_callback)(ConnectionInfo *conn);
|
||||
typedef int (*fc_rdma_request_by_buf1_callback)(ConnectionInfo *conn,
|
||||
const char *data, const int length, const int timeout_ms);
|
||||
typedef int (*fc_rdma_request_by_buf2_callback)(ConnectionInfo *conn,
|
||||
const char *data1, const int length1, const char *data2,
|
||||
const int length2, const int timeout_ms);
|
||||
typedef int (*fc_rdma_request_by_iov_callback)(ConnectionInfo *conn,
|
||||
const struct iovec *iov, const int iovcnt,
|
||||
const int timeout_ms);
|
||||
typedef int (*fc_rdma_request_by_mix_callback)(ConnectionInfo *conn,
|
||||
const char *data, const int length, const struct iovec *iov,
|
||||
const int iovcnt, const int timeout_ms);
|
||||
typedef int (*fc_rdma_send_by_buf1_callback)(ConnectionInfo *conn,
|
||||
const char *data, const int length);
|
||||
typedef int (*fc_rdma_recv_data_callback)(ConnectionInfo *conn,
|
||||
const bool call_post_recv, const int timeout_ms);
|
||||
typedef int (*fc_rdma_post_recv_callback)(ConnectionInfo *conn);
|
||||
|
||||
typedef struct {
|
||||
fc_make_connection_callback make_connection;
|
||||
fc_close_connection_callback close_connection;
|
||||
fc_is_connected_callback is_connected;
|
||||
} CommonConnectionCallbacks;
|
||||
|
||||
typedef struct {
|
||||
fc_set_busy_polling_callback set_busy_polling;
|
||||
fc_alloc_pd_callback alloc_pd;
|
||||
fc_get_connection_size_callback get_connection_size;
|
||||
fc_init_connection_callback init_connection;
|
||||
fc_make_connection_callback make_connection;
|
||||
fc_close_connection_callback close_connection;
|
||||
fc_destroy_connection_callback destroy_connection;
|
||||
fc_is_connected_callback is_connected;
|
||||
fc_send_done_callback send_done;
|
||||
|
||||
fc_rdma_get_recv_buffer_callback get_recv_buffer;
|
||||
fc_rdma_request_by_buf1_callback request_by_buf1;
|
||||
fc_rdma_request_by_buf2_callback request_by_buf2;
|
||||
fc_rdma_request_by_iov_callback request_by_iov;
|
||||
fc_rdma_request_by_mix_callback request_by_mix;
|
||||
|
||||
fc_rdma_send_by_buf1_callback send_by_buf1;
|
||||
fc_rdma_recv_data_callback recv_data;
|
||||
fc_rdma_post_recv_callback post_recv;
|
||||
} RDMAConnectionCallbacks;
|
||||
|
||||
typedef struct {
|
||||
bool inited;
|
||||
CommonConnectionCallbacks common_callbacks[2];
|
||||
RDMAConnectionCallbacks rdma_callbacks;
|
||||
} ConnectionCallbacks;
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
bool enabled;
|
||||
int htable_capacity;
|
||||
} tls; //for thread local
|
||||
|
||||
struct {
|
||||
bool double_buffers;
|
||||
int buffer_size;
|
||||
struct ibv_pd *pd;
|
||||
} rdma;
|
||||
} ConnectionExtraParams;
|
||||
|
||||
typedef int (*fc_connection_callback_func)(ConnectionInfo *conn, void *args);
|
||||
|
||||
struct tagConnectionManager;
|
||||
|
||||
typedef struct tagConnectionNode {
|
||||
|
|
@ -46,26 +152,75 @@ typedef struct tagConnectionNode {
|
|||
} ConnectionNode;
|
||||
|
||||
typedef struct tagConnectionManager {
|
||||
ConnectionNode *head;
|
||||
int total_count; //total connections
|
||||
int free_count; //free connections
|
||||
pthread_mutex_t lock;
|
||||
string_t key;
|
||||
ConnectionNode *head;
|
||||
int total_count; //total connections
|
||||
int free_count; //free connections
|
||||
struct tagConnectionManager *next;
|
||||
} ConnectionManager;
|
||||
|
||||
typedef struct tagConnectionBucket {
|
||||
ConnectionManager *head;
|
||||
pthread_mutex_t lock;
|
||||
} ConnectionBucket;
|
||||
|
||||
struct tagConnectionPool;
|
||||
|
||||
typedef struct {
|
||||
ConnectionNode **buckets;
|
||||
struct tagConnectionPool *cp;
|
||||
} ConnectionThreadHashTable;
|
||||
|
||||
typedef struct tagConnectionPool {
|
||||
HashArray hash_array; //key is ip:port, value is ConnectionManager
|
||||
pthread_mutex_t lock;
|
||||
int connect_timeout;
|
||||
struct {
|
||||
ConnectionBucket *buckets;
|
||||
uint32_t capacity;
|
||||
} hashtable;
|
||||
|
||||
int connect_timeout_ms;
|
||||
int max_count_per_entry; //0 means no limit
|
||||
|
||||
/*
|
||||
connections whose the idle time exceeds this time will be closed
|
||||
connections whose idle time exceeds this time will be closed
|
||||
unit: second
|
||||
*/
|
||||
int max_idle_time;
|
||||
int socket_domain; //socket domain
|
||||
|
||||
struct fast_mblock_man manager_allocator;
|
||||
struct fast_mblock_man node_allocator;
|
||||
|
||||
struct {
|
||||
fc_connection_callback_func func;
|
||||
void *args;
|
||||
} connect_done_callback;
|
||||
|
||||
struct {
|
||||
fc_connection_callback_func func;
|
||||
void *args;
|
||||
} validate_callback;
|
||||
|
||||
int extra_data_size;
|
||||
ConnectionExtraParams extra_params;
|
||||
pthread_key_t tls_key; //for ConnectionThreadHashTable
|
||||
} ConnectionPool;
|
||||
|
||||
typedef struct {
|
||||
int htable_capacity;
|
||||
int bucket_used;
|
||||
int server_count;
|
||||
struct {
|
||||
int total_count;
|
||||
int free_count;
|
||||
} connection;
|
||||
} ConnectionPoolStat;
|
||||
|
||||
extern ConnectionCallbacks g_connection_callbacks;
|
||||
|
||||
int conn_pool_global_init_for_rdma();
|
||||
|
||||
#define G_COMMON_CONNECTION_CALLBACKS g_connection_callbacks.common_callbacks
|
||||
#define G_RDMA_CONNECTION_CALLBACKS g_connection_callbacks.rdma_callbacks
|
||||
|
||||
/**
|
||||
* init ex function
|
||||
* parameters:
|
||||
|
|
@ -73,12 +228,43 @@ typedef struct tagConnectionPool {
|
|||
* connect_timeout: the connect timeout in seconds
|
||||
* max_count_per_entry: max connection count per host:port
|
||||
* max_idle_time: reconnect the server after max idle time in seconds
|
||||
* socket_domain: the socket domain
|
||||
* af: the socket domain
|
||||
* htable_capacity: the capacity of connection hash table
|
||||
* connect_done_func: the connect done connection callback
|
||||
* connect_done_args: the args for connect done connection callback
|
||||
* validate_func: the validate connection callback
|
||||
* validate_args: the args for validate connection callback
|
||||
* extra_data_size: the extra data size of connection
|
||||
* extra_params: for RDMA
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
int conn_pool_init_ex(ConnectionPool *cp, int connect_timeout,
|
||||
int conn_pool_init_ex1(ConnectionPool *cp, const int connect_timeout,
|
||||
const int max_count_per_entry, const int max_idle_time,
|
||||
const int socket_domain);
|
||||
const int htable_capacity, fc_connection_callback_func connect_done_func,
|
||||
void *connect_done_args, fc_connection_callback_func validate_func,
|
||||
void *validate_args, const int extra_data_size,
|
||||
const ConnectionExtraParams *extra_params);
|
||||
|
||||
/**
|
||||
* init ex function
|
||||
* parameters:
|
||||
* cp: the ConnectionPool
|
||||
* connect_timeout: the connect timeout in seconds
|
||||
* max_count_per_entry: max connection count per host:port
|
||||
* max_idle_time: reconnect the server after max idle time in seconds
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
static inline int conn_pool_init_ex(ConnectionPool *cp,
|
||||
const int connect_timeout, const int max_count_per_entry,
|
||||
const int max_idle_time)
|
||||
{
|
||||
const int htable_capacity = 0;
|
||||
const int extra_data_size = 0;
|
||||
const ConnectionExtraParams *extra_params = NULL;
|
||||
return conn_pool_init_ex1(cp, connect_timeout, max_count_per_entry,
|
||||
max_idle_time, htable_capacity, NULL, NULL, NULL, NULL,
|
||||
extra_data_size, extra_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* init function
|
||||
|
|
@ -89,8 +275,16 @@ int conn_pool_init_ex(ConnectionPool *cp, int connect_timeout,
|
|||
* max_idle_time: reconnect the server after max idle time in seconds
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
int conn_pool_init(ConnectionPool *cp, int connect_timeout,
|
||||
const int max_count_per_entry, const int max_idle_time);
|
||||
static inline int conn_pool_init(ConnectionPool *cp, const int connect_timeout,
|
||||
const int max_count_per_entry, const int max_idle_time)
|
||||
{
|
||||
const int htable_capacity = 0;
|
||||
const int extra_data_size = 0;
|
||||
const ConnectionExtraParams *extra_params = NULL;
|
||||
return conn_pool_init_ex1(cp, connect_timeout, max_count_per_entry,
|
||||
max_idle_time, htable_capacity, NULL, NULL, NULL, NULL,
|
||||
extra_data_size, extra_params);
|
||||
}
|
||||
|
||||
/**
|
||||
* destroy function
|
||||
|
|
@ -105,11 +299,17 @@ void conn_pool_destroy(ConnectionPool *cp);
|
|||
* parameters:
|
||||
* cp: the ConnectionPool
|
||||
* conn: the connection
|
||||
* service_name: the service name to log
|
||||
* shared: if the connection shared
|
||||
* err_no: return the the errno, 0 for success
|
||||
* return != NULL for success, NULL for error
|
||||
*/
|
||||
ConnectionInfo *conn_pool_get_connection(ConnectionPool *cp,
|
||||
const ConnectionInfo *conn, int *err_no);
|
||||
ConnectionInfo *conn_pool_get_connection_ex(ConnectionPool *cp,
|
||||
const ConnectionInfo *conn, const char *service_name,
|
||||
const bool shared, int *err_no);
|
||||
|
||||
#define conn_pool_get_connection(cp, conn, err_no) \
|
||||
conn_pool_get_connection_ex(cp, conn, NULL, false, err_no)
|
||||
|
||||
#define conn_pool_close_connection(cp, conn) \
|
||||
conn_pool_close_connection_ex(cp, conn, false)
|
||||
|
|
@ -122,70 +322,119 @@ ConnectionInfo *conn_pool_get_connection(ConnectionPool *cp,
|
|||
* bForce: set true to close the socket, else only push back to connection pool
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
int conn_pool_close_connection_ex(ConnectionPool *cp, ConnectionInfo *conn,
|
||||
const bool bForce);
|
||||
int conn_pool_close_connection_ex(ConnectionPool *cp,
|
||||
ConnectionInfo *conn, const bool bForce);
|
||||
|
||||
/**
|
||||
* disconnect from the server
|
||||
* parameters:
|
||||
* pConnection: the connection
|
||||
* conn: the connection
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
void conn_pool_disconnect_server(ConnectionInfo *pConnection);
|
||||
static inline void conn_pool_disconnect_server(ConnectionInfo *conn)
|
||||
{
|
||||
if (conn->sock >= 0)
|
||||
{
|
||||
close(conn->sock);
|
||||
conn->sock = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool conn_pool_is_connected(ConnectionInfo *conn)
|
||||
{
|
||||
return (conn->sock >= 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* connect to the server
|
||||
* parameters:
|
||||
* pConnection: the connection
|
||||
* connect_timeout: the connect timeout in seconds
|
||||
* service_name: the service name to log
|
||||
* connect_timeout_ms: the connect timeout in milliseconds
|
||||
* bind_ipaddr: the ip address to bind, NULL or empty for any
|
||||
* log_connect_error: if log error info when connect fail
|
||||
* NOTE: pConnection->sock will be closed when it >= 0 before connect
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
int conn_pool_connect_server_ex(ConnectionInfo *pConnection,
|
||||
const int connect_timeout, const char *bind_ipaddr,
|
||||
const bool log_connect_error);
|
||||
int conn_pool_connect_server_ex1(ConnectionInfo *conn,
|
||||
const char *service_name, const int connect_timeout_ms,
|
||||
const char *bind_ipaddr, const bool log_connect_error);
|
||||
/**
|
||||
* connect to the server
|
||||
* parameters:
|
||||
* pConnection: the connection
|
||||
* connect_timeout_ms: the connect timeout in milliseconds
|
||||
* bind_ipaddr: the ip address to bind, NULL or empty for any
|
||||
* log_connect_error: if log error info when connect fail
|
||||
* NOTE: pConnection->sock will be closed when it >= 0 before connect
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
static inline int conn_pool_connect_server_ex(ConnectionInfo *pConnection,
|
||||
const int connect_timeout_ms, const char *bind_ipaddr,
|
||||
const bool log_connect_error)
|
||||
{
|
||||
const char *service_name = NULL;
|
||||
return conn_pool_connect_server_ex1(pConnection, service_name,
|
||||
connect_timeout_ms, bind_ipaddr, log_connect_error);
|
||||
}
|
||||
|
||||
/**
|
||||
* connect to the server
|
||||
* parameters:
|
||||
* pConnection: the connection
|
||||
* connect_timeout: the connect timeout in seconds
|
||||
* connect_timeout_ms: the connect timeout in seconds
|
||||
* NOTE: pConnection->sock will be closed when it >= 0 before connect
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
static inline int conn_pool_connect_server(ConnectionInfo *pConnection,
|
||||
const int connect_timeout)
|
||||
const int connect_timeout_ms)
|
||||
{
|
||||
const char *service_name = NULL;
|
||||
const char *bind_ipaddr = NULL;
|
||||
return conn_pool_connect_server_ex(pConnection,
|
||||
connect_timeout, bind_ipaddr, true);
|
||||
return conn_pool_connect_server_ex1(pConnection, service_name,
|
||||
connect_timeout_ms, bind_ipaddr, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* connect to the server
|
||||
* parameters:
|
||||
* pConnection: the connection
|
||||
* connect_timeout: the connect timeout in seconds
|
||||
* connect_timeout_ms: the connect timeout in seconds
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
static inline int conn_pool_connect_server_anyway(ConnectionInfo *pConnection,
|
||||
const int connect_timeout)
|
||||
const int connect_timeout_ms)
|
||||
{
|
||||
const char *service_name = NULL;
|
||||
const char *bind_ipaddr = NULL;
|
||||
pConnection->sock = -1;
|
||||
return conn_pool_connect_server_ex(pConnection,
|
||||
connect_timeout, bind_ipaddr, true);
|
||||
return conn_pool_connect_server_ex1(pConnection, service_name,
|
||||
connect_timeout_ms, bind_ipaddr, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* get connection count of the pool
|
||||
* async connect to the server
|
||||
* parameters:
|
||||
* pConnection: the connection
|
||||
* bind_ipaddr: the ip address to bind, NULL or empty for any
|
||||
* NOTE: pConnection->sock will be closed when it >= 0 before connect
|
||||
* return 0 or EINPROGRESS for success, others for error
|
||||
*/
|
||||
int conn_pool_async_connect_server_ex(ConnectionInfo *conn,
|
||||
const char *bind_ipaddr);
|
||||
|
||||
#define conn_pool_async_connect_server(conn) \
|
||||
conn_pool_async_connect_server_ex(conn, NULL)
|
||||
|
||||
|
||||
/**
|
||||
* connection pool stat
|
||||
* parameters:
|
||||
* cp: the ConnectionPool
|
||||
* return current connection count
|
||||
* stat: the output stat
|
||||
* return none
|
||||
*/
|
||||
int conn_pool_get_connection_count(ConnectionPool *cp);
|
||||
void conn_pool_stat(ConnectionPool *cp, ConnectionPoolStat *stat);
|
||||
|
||||
/**
|
||||
* load server info from config file
|
||||
|
|
@ -223,13 +472,71 @@ int conn_pool_parse_server_info(const char *pServerStr,
|
|||
static inline void conn_pool_set_server_info(ConnectionInfo *pServerInfo,
|
||||
const char *ip_addr, const int port)
|
||||
{
|
||||
snprintf(pServerInfo->ip_addr, sizeof(pServerInfo->ip_addr),
|
||||
"%s", ip_addr);
|
||||
fc_safe_strcpy(pServerInfo->ip_addr, ip_addr);
|
||||
pServerInfo->port = port;
|
||||
pServerInfo->socket_domain = AF_UNSPEC;
|
||||
pServerInfo->af = is_ipv6_addr(ip_addr) ? AF_INET6 : AF_INET;
|
||||
pServerInfo->sock = -1;
|
||||
}
|
||||
|
||||
static inline int conn_pool_compare_ip_and_port(const char *ip1,
|
||||
const int port1, const char *ip2, const int port2)
|
||||
{
|
||||
int result;
|
||||
if ((result=strcmp(ip1, ip2)) != 0) {
|
||||
return result;
|
||||
}
|
||||
return port1 - port2;
|
||||
}
|
||||
|
||||
ConnectionInfo *conn_pool_alloc_connection_ex(
|
||||
const FCCommunicationType comm_type,
|
||||
const int extra_data_size,
|
||||
const ConnectionExtraParams *extra_params,
|
||||
int *err_no);
|
||||
|
||||
static inline ConnectionInfo *conn_pool_alloc_connection(
|
||||
const FCCommunicationType comm_type,
|
||||
const ConnectionExtraParams *extra_params,
|
||||
int *err_no)
|
||||
{
|
||||
const int extra_data_size = 0;
|
||||
return conn_pool_alloc_connection_ex(comm_type,
|
||||
extra_data_size, extra_params, err_no);
|
||||
}
|
||||
|
||||
static inline void conn_pool_free_connection(ConnectionInfo *conn)
|
||||
{
|
||||
free(conn);
|
||||
}
|
||||
|
||||
int conn_pool_set_rdma_extra_params_ex(ConnectionExtraParams *extra_params,
|
||||
struct fc_server_config *server_cfg, const int server_group_index,
|
||||
const bool double_buffers);
|
||||
|
||||
static inline int conn_pool_set_rdma_extra_params(
|
||||
ConnectionExtraParams *extra_params,
|
||||
struct fc_server_config *server_cfg,
|
||||
const int server_group_index)
|
||||
{
|
||||
const bool double_buffers = false;
|
||||
return conn_pool_set_rdma_extra_params_ex(extra_params,
|
||||
server_cfg, server_group_index, double_buffers);
|
||||
}
|
||||
|
||||
static inline const char *fc_comm_type_str(const FCCommunicationType type)
|
||||
{
|
||||
switch (type) {
|
||||
case fc_comm_type_sock:
|
||||
return "socket";
|
||||
case fc_comm_type_rdma:
|
||||
return "rdma";
|
||||
case fc_comm_type_both:
|
||||
return "both";
|
||||
default:
|
||||
return "unkown";
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_allocator.c
|
||||
|
||||
#include <errno.h>
|
||||
|
|
@ -10,14 +25,6 @@
|
|||
|
||||
#define BYTES_ALIGN(x, pad_mask) (((x) + pad_mask) & (~pad_mask))
|
||||
|
||||
struct allocator_wrapper {
|
||||
int alloc_bytes;
|
||||
short allocator_index;
|
||||
short magic_number;
|
||||
};
|
||||
|
||||
static struct fast_allocator_info malloc_allocator;
|
||||
|
||||
#define ADD_ALLOCATOR_TO_ARRAY(acontext, allocator, _pooled) \
|
||||
do { \
|
||||
(allocator)->index = acontext->allocator_array.count; \
|
||||
|
|
@ -25,7 +32,8 @@ static struct fast_allocator_info malloc_allocator;
|
|||
(allocator)->pooled = _pooled; \
|
||||
acontext->allocator_array.allocators[ \
|
||||
acontext->allocator_array.count++] = allocator; \
|
||||
/* logInfo("count: %d, magic_number: %d", acontext->allocator_array.count, (allocator)->magic_number); */\
|
||||
/* logInfo("count: %d, magic_number: %d", acontext->allocator_array.count, \
|
||||
(allocator)->magic_number); */ \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
|
@ -47,53 +55,72 @@ static int fast_allocator_malloc_trunk_check(const int alloc_bytes, void *args)
|
|||
acontext->allocator_array.malloc_bytes_limit ? 0 : EOVERFLOW;
|
||||
}
|
||||
|
||||
static void fast_allocator_malloc_trunk_notify_func(const int alloc_bytes, void *args)
|
||||
static void fast_allocator_malloc_trunk_notify_func(
|
||||
const enum fast_mblock_notify_type type,
|
||||
const struct fast_mblock_malloc *node, void *args)
|
||||
{
|
||||
if (alloc_bytes > 0)
|
||||
if (type == fast_mblock_notify_type_alloc)
|
||||
{
|
||||
__sync_add_and_fetch(&((struct fast_allocator_context *)args)->
|
||||
allocator_array.malloc_bytes, alloc_bytes);
|
||||
allocator_array.malloc_bytes, node->trunk_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
__sync_sub_and_fetch(&((struct fast_allocator_context *)args)->
|
||||
allocator_array.malloc_bytes, -1 * alloc_bytes);
|
||||
allocator_array.malloc_bytes, node->trunk_size);
|
||||
}
|
||||
}
|
||||
|
||||
static int allocator_array_check_capacity(struct fast_allocator_context *acontext,
|
||||
const int allocator_count)
|
||||
{
|
||||
int result;
|
||||
int bytes;
|
||||
int target_count;
|
||||
int alloc_count;
|
||||
struct fast_allocator_info **new_allocators;
|
||||
|
||||
if (acontext->allocator_array.alloc >= acontext->allocator_array.count +
|
||||
allocator_count)
|
||||
target_count = acontext->allocator_array.count + allocator_count;
|
||||
if (acontext->allocator_array.alloc >= target_count)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (acontext->allocator_array.alloc == 0)
|
||||
{
|
||||
acontext->allocator_array.alloc = 2 * allocator_count;
|
||||
if (target_count < 128)
|
||||
{
|
||||
alloc_count = 128;
|
||||
}
|
||||
else if (target_count < 256)
|
||||
{
|
||||
alloc_count = 256;
|
||||
}
|
||||
else if (target_count < 512)
|
||||
{
|
||||
alloc_count = 512;
|
||||
}
|
||||
else if (target_count < 1024)
|
||||
{
|
||||
alloc_count = 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_count = 2 * target_count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_count = acontext->allocator_array.alloc;
|
||||
do
|
||||
{
|
||||
acontext->allocator_array.alloc *= 2;
|
||||
} while (acontext->allocator_array.alloc < allocator_count);
|
||||
alloc_count *= 2;
|
||||
} while (alloc_count < target_count);
|
||||
}
|
||||
|
||||
bytes = sizeof(struct fast_allocator_info*) * acontext->allocator_array.alloc;
|
||||
new_allocators = (struct fast_allocator_info **)malloc(bytes);
|
||||
bytes = sizeof(struct fast_allocator_info *) * alloc_count;
|
||||
new_allocators = (struct fast_allocator_info **)fc_malloc(bytes);
|
||||
if (new_allocators == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, result, STRERROR(result));
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (acontext->allocator_array.allocators != NULL)
|
||||
|
|
@ -103,48 +130,73 @@ static int allocator_array_check_capacity(struct fast_allocator_context *acontex
|
|||
acontext->allocator_array.count);
|
||||
free(acontext->allocator_array.allocators);
|
||||
}
|
||||
acontext->allocator_array.alloc = alloc_count;
|
||||
acontext->allocator_array.allocators = new_allocators;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int region_init(struct fast_allocator_context *acontext,
|
||||
struct fast_region_info *region)
|
||||
const char *mblock_name_prefix, struct fast_mblock_object_callbacks
|
||||
*object_callbacks, struct fast_region_info *region)
|
||||
{
|
||||
const int64_t alloc_elements_limit = 0;
|
||||
const int prealloc_trunk_count = 0;
|
||||
int result;
|
||||
int bytes;
|
||||
int element_size;
|
||||
int allocator_count;
|
||||
struct fast_mblock_trunk_callbacks trunk_callbacks;
|
||||
struct fast_allocator_info *allocator;
|
||||
char *name;
|
||||
char name_buff[FAST_MBLOCK_NAME_SIZE];
|
||||
|
||||
region->pad_mask = region->step - 1;
|
||||
allocator_count = (region->end - region->start) / region->step;
|
||||
bytes = sizeof(struct fast_allocator_info) * allocator_count;
|
||||
region->allocators = (struct fast_allocator_info *)malloc(bytes);
|
||||
region->count = (region->end - region->start) / region->step;
|
||||
bytes = sizeof(struct fast_allocator_info) * region->count;
|
||||
region->allocators = (struct fast_allocator_info *)fc_malloc(bytes);
|
||||
if (region->allocators == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, result, STRERROR(result));
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(region->allocators, 0, bytes);
|
||||
|
||||
|
||||
if ((result=allocator_array_check_capacity(acontext, allocator_count)) != 0)
|
||||
if ((result=allocator_array_check_capacity(acontext, region->count)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (region->count == 1) {
|
||||
if (region->start == 0) {
|
||||
region->step += acontext->extra_size;
|
||||
} else {
|
||||
region->start += acontext->extra_size;
|
||||
}
|
||||
region->end += acontext->extra_size;
|
||||
}
|
||||
|
||||
trunk_callbacks.check_func = fast_allocator_malloc_trunk_check;
|
||||
trunk_callbacks.notify_func = fast_allocator_malloc_trunk_notify_func;
|
||||
name = name_buff;
|
||||
result = 0;
|
||||
allocator = region->allocators;
|
||||
for (element_size=region->start+region->step; element_size<=region->end;
|
||||
element_size+=region->step,allocator++)
|
||||
{
|
||||
result = fast_mblock_init_ex2(&allocator->mblock, NULL, element_size,
|
||||
region->alloc_elements_once, NULL, acontext->need_lock,
|
||||
fast_allocator_malloc_trunk_check,
|
||||
fast_allocator_malloc_trunk_notify_func, acontext);
|
||||
for (element_size = region->start + region->step;
|
||||
element_size <= region->end;
|
||||
element_size += region->step, allocator++)
|
||||
{
|
||||
if (mblock_name_prefix != NULL)
|
||||
{
|
||||
snprintf(name, FAST_MBLOCK_NAME_SIZE, "%s-%d",
|
||||
mblock_name_prefix, element_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
name = NULL;
|
||||
}
|
||||
|
||||
trunk_callbacks.args = acontext;
|
||||
result = fast_mblock_init_ex2(&allocator->mblock, name, element_size,
|
||||
region->alloc_elements_once, alloc_elements_limit,
|
||||
prealloc_trunk_count, object_callbacks,
|
||||
acontext->need_lock, &trunk_callbacks);
|
||||
if (result != 0)
|
||||
{
|
||||
break;
|
||||
|
|
@ -174,9 +226,11 @@ static void region_destroy(struct fast_allocator_context *acontext,
|
|||
}
|
||||
|
||||
int fast_allocator_init_ex(struct fast_allocator_context *acontext,
|
||||
const char *mblock_name_prefix, const int obj_size,
|
||||
struct fast_mblock_object_callbacks *object_callbacks,
|
||||
struct fast_region_info *regions, const int region_count,
|
||||
const int64_t alloc_bytes_limit, const double expect_usage_ratio,
|
||||
const int reclaim_interval, const bool need_lock)
|
||||
const int reclaim_interval, const bool need_lock)
|
||||
{
|
||||
int result;
|
||||
int bytes;
|
||||
|
|
@ -192,14 +246,10 @@ int fast_allocator_init_ex(struct fast_allocator_context *acontext,
|
|||
}
|
||||
|
||||
bytes = sizeof(struct fast_region_info) * region_count;
|
||||
acontext->regions = (struct fast_region_info *)malloc(bytes);
|
||||
acontext->regions = (struct fast_region_info *)fc_malloc(bytes);
|
||||
if (acontext->regions == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, result, STRERROR(result));
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
memcpy(acontext->regions, regions, bytes);
|
||||
acontext->region_count = region_count;
|
||||
|
|
@ -215,6 +265,7 @@ int fast_allocator_init_ex(struct fast_allocator_context *acontext,
|
|||
acontext->allocator_array.malloc_bytes_limit = alloc_bytes_limit /
|
||||
acontext->allocator_array.expect_usage_ratio;
|
||||
acontext->allocator_array.reclaim_interval = reclaim_interval;
|
||||
acontext->extra_size = sizeof(struct fast_allocator_wrapper) + obj_size;
|
||||
acontext->need_lock = need_lock;
|
||||
result = 0;
|
||||
previous_end = 0;
|
||||
|
|
@ -237,33 +288,46 @@ int fast_allocator_init_ex(struct fast_allocator_context *acontext,
|
|||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (pRegion->step <= 0 || !is_power2(pRegion->step))
|
||||
if (pRegion->step <= 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid step: %d",
|
||||
"invalid step: %d <= 0",
|
||||
__LINE__, pRegion->step);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (pRegion->start % pRegion->step != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid start: %d, must multiple of step: %d",
|
||||
__LINE__, pRegion->start, pRegion->step);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (pRegion->end % pRegion->step != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid end: %d, must multiple of step: %d",
|
||||
__LINE__, pRegion->end, pRegion->step);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((pRegion->end - pRegion->start) / pRegion->step > 1)
|
||||
{
|
||||
if (!is_power2(pRegion->step))
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid step: %d, expect power of 2",
|
||||
__LINE__, pRegion->step);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (pRegion->start % pRegion->step != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid start: %d, must multiple of step: %d",
|
||||
__LINE__, pRegion->start, pRegion->step);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (pRegion->end % pRegion->step != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid end: %d, must multiple of step: %d",
|
||||
__LINE__, pRegion->end, pRegion->step);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
previous_end = pRegion->end;
|
||||
|
||||
if ((result=region_init(acontext, pRegion)) != 0)
|
||||
if ((result=region_init(acontext, mblock_name_prefix,
|
||||
object_callbacks, pRegion)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
@ -279,39 +343,35 @@ int fast_allocator_init_ex(struct fast_allocator_context *acontext,
|
|||
return result;
|
||||
}
|
||||
|
||||
ADD_ALLOCATOR_TO_ARRAY(acontext, &malloc_allocator, false);
|
||||
ADD_ALLOCATOR_TO_ARRAY(acontext, &acontext->
|
||||
allocator_array.malloc_allocator, false);
|
||||
|
||||
/*
|
||||
logInfo("sizeof(struct allocator_wrapper): %d, allocator_array count: %d",
|
||||
(int)sizeof(struct allocator_wrapper), acontext->allocator_array.count);
|
||||
logInfo("sizeof(struct fast_allocator_wrapper): %d, allocator_array count: %d",
|
||||
(int)sizeof(struct fast_allocator_wrapper), acontext->allocator_array.count);
|
||||
*/
|
||||
return result;
|
||||
}
|
||||
|
||||
#define INIT_REGION(region, _start, _end, _step, _alloc_once) \
|
||||
do { \
|
||||
region.start = _start; \
|
||||
region.end = _end; \
|
||||
region.step = _step; \
|
||||
region.alloc_elements_once = _alloc_once; \
|
||||
} while(0)
|
||||
|
||||
int fast_allocator_init(struct fast_allocator_context *acontext,
|
||||
const int64_t alloc_bytes_limit, const double expect_usage_ratio,
|
||||
const int reclaim_interval, const bool need_lock)
|
||||
const char *mblock_name_prefix, const int64_t alloc_bytes_limit,
|
||||
const double expect_usage_ratio, const int reclaim_interval,
|
||||
const bool need_lock)
|
||||
{
|
||||
#define DEFAULT_REGION_COUNT 5
|
||||
|
||||
struct fast_region_info regions[DEFAULT_REGION_COUNT];
|
||||
const int obj_size = 0;
|
||||
struct fast_region_info regions[DEFAULT_REGION_COUNT];
|
||||
|
||||
INIT_REGION(regions[0], 0, 256, 8, 4096);
|
||||
INIT_REGION(regions[1], 256, 1024, 16, 1024);
|
||||
INIT_REGION(regions[2], 1024, 4096, 64, 256);
|
||||
INIT_REGION(regions[3], 4096, 16384, 256, 64);
|
||||
INIT_REGION(regions[4], 16384, 65536, 1024, 16);
|
||||
FAST_ALLOCATOR_INIT_REGION(regions[0], 0, 256, 8, 4096);
|
||||
FAST_ALLOCATOR_INIT_REGION(regions[1], 256, 1024, 16, 1024);
|
||||
FAST_ALLOCATOR_INIT_REGION(regions[2], 1024, 4096, 64, 256);
|
||||
FAST_ALLOCATOR_INIT_REGION(regions[3], 4096, 16384, 256, 64);
|
||||
FAST_ALLOCATOR_INIT_REGION(regions[4], 16384, 65536, 1024, 16);
|
||||
|
||||
return fast_allocator_init_ex(acontext, regions,
|
||||
DEFAULT_REGION_COUNT, alloc_bytes_limit,
|
||||
expect_usage_ratio, reclaim_interval, need_lock);
|
||||
return fast_allocator_init_ex(acontext, mblock_name_prefix, obj_size,
|
||||
NULL, regions, DEFAULT_REGION_COUNT, alloc_bytes_limit,
|
||||
expect_usage_ratio, reclaim_interval, need_lock);
|
||||
}
|
||||
|
||||
void fast_allocator_destroy(struct fast_allocator_context *acontext)
|
||||
|
|
@ -336,8 +396,8 @@ void fast_allocator_destroy(struct fast_allocator_context *acontext)
|
|||
memset(acontext, 0, sizeof(*acontext));
|
||||
}
|
||||
|
||||
static struct fast_allocator_info *get_allocator(struct fast_allocator_context *acontext,
|
||||
int *alloc_bytes)
|
||||
static struct fast_allocator_info *get_allocator(struct fast_allocator_context
|
||||
*acontext, int *alloc_bytes)
|
||||
{
|
||||
struct fast_region_info *pRegion;
|
||||
struct fast_region_info *region_end;
|
||||
|
|
@ -346,14 +406,19 @@ static struct fast_allocator_info *get_allocator(struct fast_allocator_context *
|
|||
for (pRegion=acontext->regions; pRegion<region_end; pRegion++)
|
||||
{
|
||||
if (*alloc_bytes <= pRegion->end)
|
||||
{
|
||||
*alloc_bytes = BYTES_ALIGN(*alloc_bytes, pRegion->pad_mask);
|
||||
return pRegion->allocators + ((*alloc_bytes -
|
||||
pRegion->start) / pRegion->step) - 1;
|
||||
}
|
||||
{
|
||||
if (pRegion->count == 1) {
|
||||
*alloc_bytes = pRegion->allocators[0].mblock.info.element_size;
|
||||
return pRegion->allocators + 0;
|
||||
} else {
|
||||
*alloc_bytes = BYTES_ALIGN(*alloc_bytes, pRegion->pad_mask);
|
||||
return pRegion->allocators + ((*alloc_bytes -
|
||||
pRegion->start) / pRegion->step) - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &malloc_allocator;
|
||||
return &acontext->allocator_array.malloc_allocator;
|
||||
}
|
||||
|
||||
int fast_allocator_retry_reclaim(struct fast_allocator_context *acontext,
|
||||
|
|
@ -372,8 +437,10 @@ int fast_allocator_retry_reclaim(struct fast_allocator_context *acontext,
|
|||
|
||||
acontext->allocator_array.last_reclaim_time = get_current_time();
|
||||
malloc_bytes = acontext->allocator_array.malloc_bytes;
|
||||
logInfo("malloc_bytes: %"PRId64", ratio: %f", malloc_bytes, (double)acontext->alloc_bytes /
|
||||
(double)malloc_bytes);
|
||||
/*
|
||||
logInfo("malloc_bytes: %"PRId64", ratio: %f", malloc_bytes,
|
||||
(double)acontext->alloc_bytes / (double)malloc_bytes);
|
||||
*/
|
||||
|
||||
if (malloc_bytes == 0 || (double)acontext->alloc_bytes /
|
||||
(double)malloc_bytes >= acontext->allocator_array.expect_usage_ratio)
|
||||
|
|
@ -381,12 +448,12 @@ int fast_allocator_retry_reclaim(struct fast_allocator_context *acontext,
|
|||
return EAGAIN;
|
||||
}
|
||||
|
||||
for (i=0; i< acontext->allocator_array.count; i++)
|
||||
for (i=0; i<acontext->allocator_array.count; i++)
|
||||
{
|
||||
if (fast_mblock_reclaim(&acontext->allocator_array.
|
||||
allocators[i]->mblock, 0, &reclaim_count, NULL) == 0)
|
||||
{
|
||||
logInfo("reclaim_count: %d", reclaim_count);
|
||||
//logInfo("reclaim_count: %d", reclaim_count);
|
||||
*total_reclaim_bytes += reclaim_count *
|
||||
acontext->allocator_array.allocators[i]->
|
||||
mblock.info.trunk_size;
|
||||
|
|
@ -396,6 +463,15 @@ int fast_allocator_retry_reclaim(struct fast_allocator_context *acontext,
|
|||
return *total_reclaim_bytes > 0 ? 0 : EAGAIN;
|
||||
}
|
||||
|
||||
static inline void malloc_trunk_notify(
|
||||
const enum fast_mblock_notify_type type,
|
||||
const int alloc_bytes, void *args)
|
||||
{
|
||||
struct fast_mblock_malloc node;
|
||||
node.trunk_size = alloc_bytes;
|
||||
fast_allocator_malloc_trunk_notify_func(type, &node, args);
|
||||
}
|
||||
|
||||
void *fast_allocator_alloc(struct fast_allocator_context *acontext,
|
||||
const int bytes)
|
||||
{
|
||||
|
|
@ -403,20 +479,21 @@ void *fast_allocator_alloc(struct fast_allocator_context *acontext,
|
|||
int64_t total_reclaim_bytes;
|
||||
struct fast_allocator_info *allocator_info;
|
||||
void *ptr;
|
||||
void *obj;
|
||||
|
||||
if (bytes < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
alloc_bytes = sizeof(struct allocator_wrapper) + bytes;
|
||||
alloc_bytes = acontext->extra_size + bytes;
|
||||
allocator_info = get_allocator(acontext, &alloc_bytes);
|
||||
if (allocator_info->pooled)
|
||||
{
|
||||
ptr = fast_mblock_alloc_object(&allocator_info->mblock);
|
||||
if (ptr == NULL)
|
||||
{
|
||||
if (acontext->allocator_array.reclaim_interval <= 0)
|
||||
if (acontext->allocator_array.reclaim_interval < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -424,7 +501,7 @@ void *fast_allocator_alloc(struct fast_allocator_context *acontext,
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
logInfo("reclaimed bytes: %"PRId64, total_reclaim_bytes);
|
||||
//logInfo("reclaimed bytes: %"PRId64, total_reclaim_bytes);
|
||||
if (total_reclaim_bytes < allocator_info->mblock.info.trunk_size)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -435,6 +512,7 @@ void *fast_allocator_alloc(struct fast_allocator_context *acontext,
|
|||
return NULL;
|
||||
}
|
||||
}
|
||||
obj = (char *)ptr + sizeof(struct fast_allocator_wrapper);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -442,34 +520,47 @@ void *fast_allocator_alloc(struct fast_allocator_context *acontext,
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
ptr = malloc(alloc_bytes);
|
||||
ptr = fc_malloc(alloc_bytes);
|
||||
if (ptr == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
fast_allocator_malloc_trunk_notify_func(alloc_bytes, acontext);
|
||||
malloc_trunk_notify(fast_mblock_notify_type_alloc,
|
||||
alloc_bytes, acontext);
|
||||
|
||||
obj = (char *)ptr + sizeof(struct fast_allocator_wrapper);
|
||||
if (acontext->allocator_array.allocators[0]->mblock.
|
||||
object_callbacks.init_func != NULL)
|
||||
{
|
||||
struct fast_mblock_man *mblock;
|
||||
mblock = &acontext->allocator_array.allocators[0]->mblock;
|
||||
mblock->object_callbacks.init_func(obj,
|
||||
mblock->object_callbacks.args);
|
||||
}
|
||||
}
|
||||
|
||||
((struct allocator_wrapper *)ptr)->allocator_index = allocator_info->index;
|
||||
((struct allocator_wrapper *)ptr)->magic_number = allocator_info->magic_number;
|
||||
((struct allocator_wrapper *)ptr)->alloc_bytes = alloc_bytes;
|
||||
|
||||
((struct fast_allocator_wrapper *)ptr)->allocator_index =
|
||||
allocator_info->index;
|
||||
((struct fast_allocator_wrapper *)ptr)->magic_number =
|
||||
allocator_info->magic_number;
|
||||
((struct fast_allocator_wrapper *)ptr)->alloc_bytes = alloc_bytes;
|
||||
__sync_add_and_fetch(&acontext->alloc_bytes, alloc_bytes);
|
||||
return (char *)ptr + sizeof(struct allocator_wrapper);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr)
|
||||
void fast_allocator_free(struct fast_allocator_context *acontext, void *obj)
|
||||
{
|
||||
struct allocator_wrapper *pWrapper;
|
||||
struct fast_allocator_wrapper *pWrapper;
|
||||
struct fast_allocator_info *allocator_info;
|
||||
void *obj;
|
||||
if (ptr == NULL)
|
||||
void *ptr;
|
||||
|
||||
if (obj == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (char *)ptr - sizeof(struct allocator_wrapper);
|
||||
pWrapper = (struct allocator_wrapper *)obj;
|
||||
ptr = (char *)obj - sizeof(struct fast_allocator_wrapper);
|
||||
pWrapper = (struct fast_allocator_wrapper *)ptr;
|
||||
if (pWrapper->allocator_index < 0 || pWrapper->allocator_index >=
|
||||
acontext->allocator_array.count)
|
||||
{
|
||||
|
|
@ -479,7 +570,8 @@ void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr)
|
|||
return;
|
||||
}
|
||||
|
||||
allocator_info = acontext->allocator_array.allocators[pWrapper->allocator_index];
|
||||
allocator_info = acontext->allocator_array.
|
||||
allocators[pWrapper->allocator_index];
|
||||
if (pWrapper->magic_number != allocator_info->magic_number)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
|
|
@ -494,12 +586,36 @@ void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr)
|
|||
pWrapper->magic_number = 0;
|
||||
if (allocator_info->pooled)
|
||||
{
|
||||
fast_mblock_free_object(&allocator_info->mblock, obj);
|
||||
fast_mblock_free_object(&allocator_info->mblock, ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
fast_allocator_malloc_trunk_notify_func(-1 * pWrapper->alloc_bytes, acontext);
|
||||
free(obj);
|
||||
}
|
||||
{
|
||||
malloc_trunk_notify(fast_mblock_notify_type_reclaim,
|
||||
pWrapper->alloc_bytes, acontext);
|
||||
|
||||
if (acontext->allocator_array.allocators[0]->mblock.
|
||||
object_callbacks.destroy_func != NULL)
|
||||
{
|
||||
struct fast_mblock_man *mblock;
|
||||
mblock = &acontext->allocator_array.allocators[0]->mblock;
|
||||
mblock->object_callbacks.destroy_func(obj,
|
||||
mblock->object_callbacks.args);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
char *fast_allocator_memdup(struct fast_allocator_context *acontext,
|
||||
const char *src, const int len)
|
||||
{
|
||||
char *dest;
|
||||
dest = (char *)fast_allocator_alloc(acontext, len);
|
||||
if (dest == NULL) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail", __LINE__, len);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(dest, src, len);
|
||||
return dest;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_allocator.h
|
||||
|
||||
|
|
@ -28,38 +35,55 @@ struct fast_allocator_info
|
|||
|
||||
struct fast_region_info
|
||||
{
|
||||
int start;
|
||||
int end;
|
||||
int start; //exclude
|
||||
int end; //include
|
||||
int step;
|
||||
int alloc_elements_once;
|
||||
int pad_mask; //for internal use
|
||||
int count;
|
||||
struct fast_allocator_info *allocators;
|
||||
};
|
||||
|
||||
struct fast_allocator_array
|
||||
{
|
||||
int count;
|
||||
int alloc;
|
||||
int reclaim_interval; //<= 0 for never reclaim
|
||||
int last_reclaim_time;
|
||||
volatile int64_t malloc_bytes; //total alloc bytes
|
||||
int64_t malloc_bytes_limit; //water mark bytes for malloc
|
||||
double expect_usage_ratio;
|
||||
struct fast_allocator_info **allocators;
|
||||
int count;
|
||||
int alloc;
|
||||
int reclaim_interval; //< 0 for never reclaim
|
||||
int last_reclaim_time;
|
||||
volatile int64_t malloc_bytes; //total alloc bytes
|
||||
int64_t malloc_bytes_limit; //water mark bytes for malloc
|
||||
double expect_usage_ratio;
|
||||
struct fast_allocator_info malloc_allocator;
|
||||
struct fast_allocator_info **allocators;
|
||||
};
|
||||
|
||||
struct fast_allocator_wrapper {
|
||||
int alloc_bytes;
|
||||
short allocator_index;
|
||||
short magic_number;
|
||||
};
|
||||
|
||||
struct fast_allocator_context
|
||||
{
|
||||
struct fast_region_info *regions;
|
||||
int region_count;
|
||||
int extra_size;
|
||||
|
||||
struct fast_allocator_array allocator_array;
|
||||
|
||||
int64_t alloc_bytes_limit; //mater mark bytes for alloc
|
||||
int64_t alloc_bytes_limit; //water mark bytes for alloc
|
||||
volatile int64_t alloc_bytes; //total alloc bytes
|
||||
bool need_lock; //if need mutex lock for acontext
|
||||
};
|
||||
|
||||
#define FAST_ALLOCATOR_INIT_REGION(region, _start, _end, _step, _alloc_once) \
|
||||
do { \
|
||||
(region).start = _start; \
|
||||
(region).end = _end; \
|
||||
(region).step = _step; \
|
||||
(region).alloc_elements_once = _alloc_once; \
|
||||
} while(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -68,29 +92,36 @@ extern "C" {
|
|||
allocator init by default region allocators
|
||||
parameters:
|
||||
acontext: the context pointer
|
||||
alloc_bytes_limit: the alloc limit, 0 for no limit
|
||||
mblock_name_prefix: the name prefix of mblock
|
||||
alloc_bytes_limit: the alloc limit, 0 for no limit
|
||||
expect_usage_ratio: the trunk usage ratio
|
||||
reclaim_interval: reclaim interval in second, 0 for never reclaim
|
||||
reclaim_interval: reclaim interval in second, < 0 for never reclaim
|
||||
need_lock: if need lock
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_allocator_init(struct fast_allocator_context *acontext,
|
||||
const int64_t alloc_bytes_limit, const double expect_usage_ratio,
|
||||
const int reclaim_interval, const bool need_lock);
|
||||
const char *mblock_name_prefix, const int64_t alloc_bytes_limit,
|
||||
const double expect_usage_ratio, const int reclaim_interval,
|
||||
const bool need_lock);
|
||||
|
||||
/**
|
||||
allocator init
|
||||
parameters:
|
||||
acontext: the context pointer
|
||||
mblock_name_prefix: name prefix of object alloctors
|
||||
obj_size: element size of object as sizeof(obj)
|
||||
object_callbacks: object init and destroy callbacks
|
||||
regions: the region array
|
||||
region_count: the region count
|
||||
alloc_bytes_limit: the alloc limit, 0 for no limit
|
||||
alloc_bytes_limit: the alloc limit, 0 for no limit
|
||||
expect_usage_ratio: the trunk usage ratio
|
||||
reclaim_interval: reclaim interval in second, 0 for never reclaim
|
||||
reclaim_interval: reclaim interval in second, < 0 for never reclaim
|
||||
need_lock: if need lock
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_allocator_init_ex(struct fast_allocator_context *acontext,
|
||||
const char *mblock_name_prefix, const int obj_size,
|
||||
struct fast_mblock_object_callbacks *object_callbacks,
|
||||
struct fast_region_info *regions, const int region_count,
|
||||
const int64_t alloc_bytes_limit, const double expect_usage_ratio,
|
||||
const int reclaim_interval, const bool need_lock);
|
||||
|
|
@ -116,10 +147,10 @@ void* fast_allocator_alloc(struct fast_allocator_context *acontext,
|
|||
free a node (put a node to the context)
|
||||
parameters:
|
||||
acontext: the context pointer
|
||||
ptr: the pointer to free
|
||||
obj: the object ptr to free
|
||||
return none
|
||||
*/
|
||||
void fast_allocator_free(struct fast_allocator_context *acontext, void *ptr);
|
||||
void fast_allocator_free(struct fast_allocator_context *acontext, void *obj);
|
||||
|
||||
/**
|
||||
retry reclaim free trunks
|
||||
|
|
@ -131,9 +162,48 @@ return error no, 0 for success, != 0 fail
|
|||
int fast_allocator_retry_reclaim(struct fast_allocator_context *acontext,
|
||||
int64_t *total_reclaim_bytes);
|
||||
|
||||
char *fast_allocator_memdup(struct fast_allocator_context *acontext,
|
||||
const char *src, const int len);
|
||||
|
||||
static inline char *fast_allocator_strdup_ex(struct fast_allocator_context *
|
||||
acontext, const char *src, const int len)
|
||||
{
|
||||
return fast_allocator_memdup(acontext, src, len + 1);
|
||||
}
|
||||
|
||||
static inline char *fast_allocator_strdup(struct fast_allocator_context *
|
||||
acontext, const char *src)
|
||||
{
|
||||
return fast_allocator_memdup(acontext, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
static inline int fast_allocator_alloc_string_ex(struct fast_allocator_context
|
||||
*acontext, string_t *dest, const char *src, const int len)
|
||||
{
|
||||
dest->str = fast_allocator_memdup(acontext, src, len);
|
||||
dest->len = len;
|
||||
return dest->str != NULL ? 0 : ENOMEM;
|
||||
}
|
||||
|
||||
static inline int fast_allocator_alloc_string(struct fast_allocator_context
|
||||
*acontext, string_t *dest, const string_t *src)
|
||||
{
|
||||
return fast_allocator_alloc_string_ex(acontext, dest, src->str, src->len);
|
||||
}
|
||||
|
||||
static inline int64_t fast_allocator_avail_memory(
|
||||
struct fast_allocator_context *acontext)
|
||||
{
|
||||
if (acontext->alloc_bytes_limit == 0) {
|
||||
return INT64_MIN;
|
||||
}
|
||||
|
||||
return acontext->alloc_bytes_limit - __sync_add_and_fetch(
|
||||
&acontext->allocator_array.malloc_bytes, 0);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_blocked_queue.c
|
||||
|
||||
#include <errno.h>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_blocked_queue.h
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -8,27 +23,31 @@
|
|||
#include <sys/stat.h>
|
||||
#include "logger.h"
|
||||
#include "shared_func.h"
|
||||
#include "fc_memory.h"
|
||||
#include "fast_buffer.h"
|
||||
|
||||
int fast_buffer_init_ex(FastBuffer *buffer, const int init_capacity)
|
||||
int fast_buffer_init_ex(FastBuffer *buffer, const int init_capacity,
|
||||
const bool binary_mode, const bool check_capacity)
|
||||
{
|
||||
buffer->length = 0;
|
||||
buffer->binary_mode = binary_mode;
|
||||
if (init_capacity > 0)
|
||||
{
|
||||
buffer->alloc_size = init_capacity;
|
||||
buffer->check_capacity = check_capacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->alloc_size = 256;
|
||||
buffer->check_capacity = true;
|
||||
}
|
||||
buffer->data = (char *)malloc(buffer->alloc_size);
|
||||
buffer->data = (char *)fc_malloc(buffer->alloc_size);
|
||||
if (buffer->data == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail", __LINE__, buffer->alloc_size);
|
||||
return ENOMEM;
|
||||
}
|
||||
*(buffer->data) = '\0';
|
||||
|
||||
fast_buffer_set_null_terminator(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -42,32 +61,41 @@ void fast_buffer_destroy(FastBuffer *buffer)
|
|||
}
|
||||
}
|
||||
|
||||
int fast_buffer_check(FastBuffer *buffer, const int inc_len)
|
||||
int fast_buffer_set_capacity(FastBuffer *buffer, const int capacity)
|
||||
{
|
||||
int alloc_size;
|
||||
int new_capacity;
|
||||
char *buff;
|
||||
|
||||
if (buffer->alloc_size > buffer->length + inc_len)
|
||||
{
|
||||
return 0;
|
||||
new_capacity = FC_MAX(capacity, buffer->length + 1);
|
||||
if (buffer->alloc_size >= new_capacity) {
|
||||
if (new_capacity > 1024) {
|
||||
alloc_size = 2048;
|
||||
} else if (new_capacity > 512) {
|
||||
alloc_size = 1024;
|
||||
} else if (new_capacity > 256) {
|
||||
alloc_size = 512;
|
||||
} else {
|
||||
alloc_size = 256;
|
||||
}
|
||||
} else {
|
||||
alloc_size = buffer->alloc_size * 2;
|
||||
}
|
||||
alloc_size = buffer->alloc_size * 2;
|
||||
while (alloc_size <= buffer->length + inc_len)
|
||||
{
|
||||
|
||||
while (alloc_size < new_capacity) {
|
||||
alloc_size *= 2;
|
||||
}
|
||||
|
||||
buff = (char *)malloc(alloc_size);
|
||||
if (buff == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail", __LINE__, alloc_size);
|
||||
buff = (char *)fc_malloc(alloc_size);
|
||||
if (buff == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (buffer->length > 0)
|
||||
{
|
||||
if (buffer->length > 0) {
|
||||
memcpy(buff, buffer->data, buffer->length);
|
||||
if (!buffer->binary_mode) {
|
||||
*(buff + buffer->length) = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer->data);
|
||||
|
|
@ -97,7 +125,7 @@ int fast_buffer_append(FastBuffer *buffer, const char *format, ...)
|
|||
}
|
||||
else //maybe full, realloc and try again
|
||||
{
|
||||
if ((result=fast_buffer_check(buffer, len)) == 0)
|
||||
if ((result=fast_buffer_check(buffer, len + 1)) == 0)
|
||||
{
|
||||
va_start(ap, format);
|
||||
buffer->length += vsnprintf(buffer->data + buffer->length,
|
||||
|
|
@ -106,7 +134,7 @@ int fast_buffer_append(FastBuffer *buffer, const char *format, ...)
|
|||
}
|
||||
else
|
||||
{
|
||||
*(buffer->data + buffer->length) = '\0'; //restore
|
||||
fast_buffer_set_null_terminator(buffer); //restore
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
@ -116,6 +144,26 @@ int fast_buffer_append_buff(FastBuffer *buffer, const char *data, const int len)
|
|||
{
|
||||
int result;
|
||||
|
||||
if (len <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((result=fast_buffer_check(buffer, len + 1)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
memcpy(buffer->data + buffer->length, data, len);
|
||||
buffer->length += len;
|
||||
fast_buffer_set_null_terminator(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fast_buffer_append_binary(FastBuffer *buffer,
|
||||
const void *data, const int len)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (len <= 0)
|
||||
{
|
||||
return 0;
|
||||
|
|
@ -127,33 +175,6 @@ int fast_buffer_append_buff(FastBuffer *buffer, const char *data, const int len)
|
|||
|
||||
memcpy(buffer->data + buffer->length, data, len);
|
||||
buffer->length += len;
|
||||
*(buffer->data + buffer->length) = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fast_buffer_append_int(FastBuffer *buffer, const int n)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=fast_buffer_check(buffer, 16)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer->length += sprintf(buffer->data + buffer->length, "%d", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fast_buffer_append_int64(FastBuffer *buffer, const int64_t n)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=fast_buffer_check(buffer, 32)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer->length += sprintf(buffer->data + buffer->length, "%"PRId64, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FAST_BUFFER_H__
|
||||
#define __FAST_BUFFER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "common_define.h"
|
||||
#include "shared_func.h"
|
||||
|
||||
typedef struct fast_buffer {
|
||||
char *data;
|
||||
int alloc_size;
|
||||
int length;
|
||||
char *data;
|
||||
int alloc_size;
|
||||
int length;
|
||||
bool binary_mode;
|
||||
bool check_capacity;
|
||||
} FastBuffer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
@ -24,32 +41,110 @@ static inline char *fast_buffer_data(FastBuffer *buffer)
|
|||
return buffer->data;
|
||||
}
|
||||
|
||||
int fast_buffer_init_ex(FastBuffer *buffer, const int init_capacity);
|
||||
int fast_buffer_init_ex(FastBuffer *buffer, const int init_capacity,
|
||||
const bool binary_mode, const bool check_capacity);
|
||||
|
||||
static inline int fast_buffer_init1(FastBuffer *buffer, const int init_capacity)
|
||||
{
|
||||
const bool binary_mode = false;
|
||||
const bool check_capacity = true;
|
||||
return fast_buffer_init_ex(buffer, init_capacity,
|
||||
binary_mode, check_capacity);
|
||||
}
|
||||
|
||||
static inline int fast_buffer_init(FastBuffer *buffer)
|
||||
{
|
||||
return fast_buffer_init_ex(buffer, 0);
|
||||
const int init_capacity = 0;
|
||||
return fast_buffer_init1(buffer, init_capacity);
|
||||
}
|
||||
|
||||
#define fast_buffer_set_null_terminator(buffer) \
|
||||
if (!buffer->binary_mode) *(buffer->data + buffer->length) = '\0'
|
||||
|
||||
#define fast_buffer_check(buffer, inc_len) \
|
||||
((buffer)->check_capacity ? fast_buffer_check_inc_size(buffer, inc_len) : 0)
|
||||
|
||||
#define fast_buffer_clear(buffer) fast_buffer_reset(buffer)
|
||||
|
||||
static inline void fast_buffer_reset(FastBuffer *buffer)
|
||||
{
|
||||
buffer->length = 0;
|
||||
*buffer->data = '\0';
|
||||
fast_buffer_set_null_terminator(buffer);
|
||||
}
|
||||
|
||||
void fast_buffer_destroy(FastBuffer *buffer);
|
||||
|
||||
int fast_buffer_check(FastBuffer *buffer, const int inc_len);
|
||||
int fast_buffer_set_capacity(FastBuffer *buffer, const int capacity);
|
||||
|
||||
static inline int fast_buffer_check_capacity(FastBuffer *buffer,
|
||||
const int capacity)
|
||||
{
|
||||
if (buffer->alloc_size >= capacity)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fast_buffer_set_capacity(buffer, capacity);
|
||||
}
|
||||
|
||||
static inline int fast_buffer_check_inc_size(FastBuffer *buffer,
|
||||
const int inc_size)
|
||||
{
|
||||
return fast_buffer_check_capacity(buffer, buffer->length + inc_size);
|
||||
}
|
||||
|
||||
int fast_buffer_append(FastBuffer *buffer, const char *format, ...);
|
||||
|
||||
int fast_buffer_append_buff(FastBuffer *buffer, const char *data, const int len);
|
||||
int fast_buffer_append_buff(FastBuffer *buffer,
|
||||
const char *data, const int len);
|
||||
|
||||
int fast_buffer_append_int(FastBuffer *buffer, const int n);
|
||||
int fast_buffer_append_binary(FastBuffer *buffer,
|
||||
const void *data, const int len);
|
||||
|
||||
int fast_buffer_append_int64(FastBuffer *buffer, const int64_t n);
|
||||
static inline int fast_buffer_append_char(FastBuffer *buffer, const char ch)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=fast_buffer_check(buffer, 1)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
*(buffer->data + buffer->length++) = ch;
|
||||
fast_buffer_set_null_terminator(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fast_buffer_append_int32(FastBuffer *buffer, const int n)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=fast_buffer_check(buffer, 16)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer->length += fc_itoa(n, buffer->data + buffer->length);
|
||||
fast_buffer_set_null_terminator(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fast_buffer_append_int64(FastBuffer *buffer, const int64_t n)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=fast_buffer_check(buffer, 32)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer->length += fc_itoa(n, buffer->data + buffer->length);
|
||||
fast_buffer_set_null_terminator(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define fast_buffer_append_int(buffer, n) \
|
||||
fast_buffer_append_int64(buffer, n)
|
||||
|
||||
int fast_buffer_append_file(FastBuffer *buffer, const char *filename);
|
||||
|
||||
|
|
@ -73,4 +168,3 @@ static inline int fast_buffer_append_buffer(FastBuffer *buffer, FastBuffer *src)
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_mblock.h
|
||||
|
||||
|
|
@ -16,12 +23,26 @@
|
|||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "common_define.h"
|
||||
#include "chain.h"
|
||||
#include "fc_memory.h"
|
||||
#include "logger.h"
|
||||
|
||||
/* following two macros for debug only */
|
||||
/*
|
||||
#define FAST_MBLOCK_MAGIC_CHECK 1
|
||||
#define FAST_MBLOCK_MAGIC_NUMBER 1234567890
|
||||
*/
|
||||
|
||||
|
||||
#define FAST_MBLOCK_NAME_SIZE 32
|
||||
|
||||
#define FAST_MBLOCK_ORDER_BY_ALLOC_BYTES 1
|
||||
#define FAST_MBLOCK_ORDER_BY_ELEMENT_SIZE 2
|
||||
#define FAST_MBLOCK_ORDER_BY_USED_RATIO 3
|
||||
|
||||
enum fast_mblock_notify_type {
|
||||
fast_mblock_notify_type_alloc,
|
||||
fast_mblock_notify_type_reclaim,
|
||||
};
|
||||
|
||||
/* free node chain */
|
||||
struct fast_mblock_node
|
||||
|
|
@ -29,13 +50,19 @@ struct fast_mblock_node
|
|||
struct fast_mblock_node *next;
|
||||
int offset; //trunk offset
|
||||
int recycle_timestamp;
|
||||
#ifdef FAST_MBLOCK_MAGIC_CHECK
|
||||
int index;
|
||||
int magic; //magic number
|
||||
#endif
|
||||
char data[0]; //the data buffer
|
||||
};
|
||||
|
||||
/* malloc chain */
|
||||
struct fast_mblock_malloc
|
||||
{
|
||||
int64_t ref_count; //refference count
|
||||
int64_t ref_count; //refference count
|
||||
int alloc_count; //allocated element count
|
||||
int trunk_size; //trunk bytes
|
||||
struct fast_mblock_malloc *prev;
|
||||
struct fast_mblock_malloc *next;
|
||||
};
|
||||
|
|
@ -45,24 +72,31 @@ struct fast_mblock_chain {
|
|||
struct fast_mblock_node *tail;
|
||||
};
|
||||
|
||||
typedef int (*fast_mblock_alloc_init_func)(void *element);
|
||||
/* call by alloc trunk */
|
||||
typedef int (*fast_mblock_object_init_func)(void *element, void *args);
|
||||
|
||||
/* call by free trunk */
|
||||
typedef void (*fast_mblock_object_destroy_func)(void *element, void *args);
|
||||
|
||||
typedef int (*fast_mblock_malloc_trunk_check_func)(
|
||||
const int alloc_bytes, void *args);
|
||||
|
||||
typedef void (*fast_mblock_malloc_trunk_notify_func)(
|
||||
const int alloc_bytes, void *args);
|
||||
const enum fast_mblock_notify_type type,
|
||||
const struct fast_mblock_malloc *node, void *args);
|
||||
|
||||
struct fast_mblock_info
|
||||
{
|
||||
char name[FAST_MBLOCK_NAME_SIZE];
|
||||
int element_size; //element size
|
||||
int element_total_count; //total element count
|
||||
int element_used_count; //used element count
|
||||
int trunk_size; //trunk size
|
||||
int trunk_total_count; //total trunk count
|
||||
int trunk_used_count; //used trunk count
|
||||
int instance_count; //instance count
|
||||
int block_size;
|
||||
int64_t element_total_count; //total element count
|
||||
int64_t element_used_count; //used element count
|
||||
int64_t delay_free_elements; //delay free element count
|
||||
int64_t trunk_total_count; //total trunk count
|
||||
int64_t trunk_used_count; //used trunk count
|
||||
};
|
||||
|
||||
struct fast_mblock_trunks
|
||||
|
|
@ -70,7 +104,13 @@ struct fast_mblock_trunks
|
|||
struct fast_mblock_malloc head; //malloc chain to be freed
|
||||
};
|
||||
|
||||
struct fast_mblock_malloc_trunk_callback
|
||||
struct fast_mblock_object_callbacks {
|
||||
fast_mblock_object_init_func init_func;
|
||||
fast_mblock_object_destroy_func destroy_func;
|
||||
void *args;
|
||||
};
|
||||
|
||||
struct fast_mblock_trunk_callbacks
|
||||
{
|
||||
fast_mblock_malloc_trunk_check_func check_func;
|
||||
fast_mblock_malloc_trunk_notify_func notify_func;
|
||||
|
|
@ -80,24 +120,35 @@ struct fast_mblock_malloc_trunk_callback
|
|||
struct fast_mblock_man
|
||||
{
|
||||
struct fast_mblock_info info;
|
||||
int alloc_elements_once; //alloc elements once
|
||||
struct fast_mblock_node *free_chain_head; //free node chain
|
||||
struct {
|
||||
bool need_wait;
|
||||
int exceed_log_level; //for exceed limit
|
||||
int once; //alloc elements once
|
||||
int64_t limit; //<= 0 for no limit
|
||||
bool *pcontinue_flag;
|
||||
} alloc_elements;
|
||||
|
||||
struct {
|
||||
struct fast_mblock_node *head;
|
||||
} freelist; //free node chain
|
||||
|
||||
struct fast_mblock_trunks trunks;
|
||||
struct fast_mblock_chain delay_free_chain; //delay free node chain
|
||||
|
||||
fast_mblock_alloc_init_func alloc_init_func;
|
||||
struct fast_mblock_malloc_trunk_callback malloc_trunk_callback;
|
||||
struct fast_mblock_object_callbacks object_callbacks;
|
||||
struct fast_mblock_trunk_callbacks trunk_callbacks;
|
||||
|
||||
bool need_lock; //if need mutex lock
|
||||
pthread_mutex_t lock; //the lock for read / write free node chain
|
||||
bool need_lock; //if need mutex lock
|
||||
pthread_lock_cond_pair_t lcp; //for read / write free node chain
|
||||
struct fast_mblock_man *prev; //for stat manager
|
||||
struct fast_mblock_man *next; //for stat manager
|
||||
};
|
||||
|
||||
#define GET_BLOCK_SIZE(info) \
|
||||
(MEM_ALIGN(sizeof(struct fast_mblock_node) + (info).element_size))
|
||||
#define fast_mblock_get_block_size(element_size) \
|
||||
(MEM_ALIGN(sizeof(struct fast_mblock_node) + element_size))
|
||||
|
||||
#define fast_mblock_get_block_size(mblock) GET_BLOCK_SIZE(mblock->info)
|
||||
#define fast_mblock_get_trunk_size(block_size, element_count) \
|
||||
(sizeof(struct fast_mblock_malloc) + block_size * element_count)
|
||||
|
||||
#define fast_mblock_to_node_ptr(data_ptr) \
|
||||
(struct fast_mblock_node *)((char *)data_ptr - ((size_t)(char *) \
|
||||
|
|
@ -108,21 +159,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#define fast_mblock_init(mblock, element_size, alloc_elements_once) \
|
||||
fast_mblock_init_ex(mblock, element_size, alloc_elements_once, NULL, true)
|
||||
|
||||
/**
|
||||
mblock init
|
||||
parameters:
|
||||
mblock: the mblock pointer
|
||||
element_size: element size, such as sizeof(struct xxx)
|
||||
alloc_elements_once: malloc elements once, 0 for malloc 1MB memory once
|
||||
init_func: the init function
|
||||
need_lock: if need lock
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_mblock_init_ex(struct fast_mblock_man *mblock,
|
||||
const int element_size, const int alloc_elements_once,
|
||||
fast_mblock_alloc_init_func init_func, const bool need_lock);
|
||||
fast_mblock_init_ex(mblock, element_size, alloc_elements_once, \
|
||||
0, NULL, NULL, true)
|
||||
|
||||
/**
|
||||
mblock init
|
||||
|
|
@ -131,19 +169,19 @@ parameters:
|
|||
mblock: the mblock pointer
|
||||
element_size: element size, such as sizeof(struct xxx)
|
||||
alloc_elements_once: malloc elements once, 0 for malloc 1MB memory once
|
||||
init_func: the init function
|
||||
alloc_elements_limit: malloc elements limit, <= 0 for no limit
|
||||
prealloc_trunk_count: prealloc trunk node count
|
||||
object_callbacks: the object callback functions and args
|
||||
need_lock: if need lock
|
||||
malloc_trunk_check: the malloc trunk check function pointor
|
||||
malloc_trunk_notify: the malloc trunk notify function pointor
|
||||
malloc_trunk_args: the malloc trunk args
|
||||
trunk_callbacks: the trunk callback functions and args
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_mblock_init_ex2(struct fast_mblock_man *mblock, const char *name,
|
||||
const int element_size, const int alloc_elements_once,
|
||||
fast_mblock_alloc_init_func init_func, const bool need_lock,
|
||||
fast_mblock_malloc_trunk_check_func malloc_trunk_check,
|
||||
fast_mblock_malloc_trunk_notify_func malloc_trunk_notify,
|
||||
void *malloc_trunk_args);
|
||||
const int64_t alloc_elements_limit, const int prealloc_trunk_count,
|
||||
struct fast_mblock_object_callbacks *object_callbacks,
|
||||
const bool need_lock, struct fast_mblock_trunk_callbacks
|
||||
*trunk_callbacks);
|
||||
|
||||
/**
|
||||
mblock init
|
||||
|
|
@ -152,18 +190,52 @@ parameters:
|
|||
mblock: the mblock pointer
|
||||
element_size: element size, such as sizeof(struct xxx)
|
||||
alloc_elements_once: malloc elements once, 0 for malloc 1MB memory once
|
||||
init_func: the init function
|
||||
alloc_elements_limit: malloc elements limit, <= 0 for no limit
|
||||
init_func: the object init function
|
||||
init_args: the args for object init function
|
||||
need_lock: if need lock
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
static inline int fast_mblock_init_ex1(struct fast_mblock_man *mblock,
|
||||
const char *name, const int element_size, const int alloc_elements_once,
|
||||
fast_mblock_alloc_init_func init_func, const bool need_lock)
|
||||
const char *name, const int element_size,
|
||||
const int alloc_elements_once, const int64_t alloc_elements_limit,
|
||||
fast_mblock_object_init_func init_func, void *init_args,
|
||||
const bool need_lock)
|
||||
{
|
||||
const int prealloc_trunk_count = 0;
|
||||
struct fast_mblock_object_callbacks object_callbacks;
|
||||
|
||||
object_callbacks.init_func = init_func;
|
||||
object_callbacks.destroy_func = NULL;
|
||||
object_callbacks.args = init_args;
|
||||
return fast_mblock_init_ex2(mblock, name, element_size,
|
||||
alloc_elements_once, init_func, need_lock, NULL, NULL, NULL);
|
||||
alloc_elements_once, alloc_elements_limit,
|
||||
prealloc_trunk_count, &object_callbacks,
|
||||
need_lock, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
mblock init
|
||||
parameters:
|
||||
mblock: the mblock pointer
|
||||
element_size: element size, such as sizeof(struct xxx)
|
||||
alloc_elements_once: malloc elements once, 0 for malloc 1MB memory once
|
||||
alloc_elements_limit: malloc elements limit, <= 0 for no limit
|
||||
object_callbacks: the object callback functions and args
|
||||
need_lock: if need lock
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
static inline int fast_mblock_init_ex(struct fast_mblock_man *mblock,
|
||||
const int element_size, const int alloc_elements_once,
|
||||
const int64_t alloc_elements_limit, fast_mblock_object_init_func
|
||||
init_func, void *init_args, const bool need_lock)
|
||||
{
|
||||
return fast_mblock_init_ex1(mblock, NULL, element_size,
|
||||
alloc_elements_once, alloc_elements_limit,
|
||||
init_func, init_args, need_lock);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
mblock destroy
|
||||
parameters:
|
||||
|
|
@ -171,6 +243,35 @@ parameters:
|
|||
*/
|
||||
void fast_mblock_destroy(struct fast_mblock_man *mblock);
|
||||
|
||||
static inline int fast_mblock_set_need_wait(struct fast_mblock_man *mblock,
|
||||
const bool need_wait, bool * volatile pcontinue_flag)
|
||||
{
|
||||
if (!mblock->need_lock || mblock->alloc_elements.limit <= 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"need_lock: %d != 1 or alloc_elements.limit: %"PRId64" <= 0",
|
||||
__LINE__, mblock->need_lock, mblock->alloc_elements.limit);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
mblock->alloc_elements.need_wait = need_wait;
|
||||
mblock->alloc_elements.pcontinue_flag = pcontinue_flag;
|
||||
if (need_wait)
|
||||
{
|
||||
mblock->alloc_elements.exceed_log_level = LOG_NOTHING;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void fast_mblock_set_exceed_log_level(
|
||||
struct fast_mblock_man *mblock, const int log_level)
|
||||
{
|
||||
mblock->alloc_elements.exceed_log_level = log_level;
|
||||
}
|
||||
|
||||
#define fast_mblock_set_exceed_silence(mblock) \
|
||||
fast_mblock_set_exceed_log_level(mblock, LOG_NOTHING)
|
||||
|
||||
/**
|
||||
alloc a node from the mblock
|
||||
parameters:
|
||||
|
|
@ -189,6 +290,45 @@ return 0 for success, return none zero if fail
|
|||
int fast_mblock_free(struct fast_mblock_man *mblock,
|
||||
struct fast_mblock_node *pNode);
|
||||
|
||||
/**
|
||||
batch alloc nodes from the mblock
|
||||
parameters:
|
||||
mblock: the mblock pointer
|
||||
count: alloc count
|
||||
chain: return the mblock node chain
|
||||
return 0 for success, return none zero if fail
|
||||
*/
|
||||
int fast_mblock_batch_alloc(struct fast_mblock_man *mblock,
|
||||
const int count, struct fast_mblock_chain *chain);
|
||||
|
||||
/**
|
||||
batch alloc nodes from the mblock
|
||||
parameters:
|
||||
mblock: the mblock pointer
|
||||
count: alloc count
|
||||
return the alloced node head, return NULL if fail
|
||||
*/
|
||||
static inline struct fast_mblock_node *fast_mblock_batch_alloc1(
|
||||
struct fast_mblock_man *mblock, const int count)
|
||||
{
|
||||
struct fast_mblock_chain chain;
|
||||
if (fast_mblock_batch_alloc(mblock, count, &chain) == 0) {
|
||||
return chain.head;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
batch free nodes
|
||||
parameters:
|
||||
mblock: the mblock pointer
|
||||
chain: the node chain to free
|
||||
return 0 for success, return none zero if fail
|
||||
*/
|
||||
int fast_mblock_batch_free(struct fast_mblock_man *mblock,
|
||||
struct fast_mblock_chain *chain);
|
||||
|
||||
/**
|
||||
delay free a node (put a node to the mblock)
|
||||
parameters:
|
||||
|
|
@ -230,6 +370,17 @@ static inline int fast_mblock_free_object(struct fast_mblock_man *mblock,
|
|||
return fast_mblock_free(mblock, fast_mblock_to_node_ptr(object));
|
||||
}
|
||||
|
||||
/**
|
||||
free objects (put objects to the mblock)
|
||||
parameters:
|
||||
mblock: the mblock pointer
|
||||
objs: the object array to free
|
||||
count: the count of the object array
|
||||
return none
|
||||
*/
|
||||
void fast_mblock_free_objects(struct fast_mblock_man *mblock,
|
||||
void **objs, const int count);
|
||||
|
||||
/**
|
||||
delay free a object (put a node to the mblock)
|
||||
parameters:
|
||||
|
|
@ -260,7 +411,7 @@ return the delay free node count of the mblock, return -1 if fail
|
|||
*/
|
||||
int fast_mblock_delay_free_count(struct fast_mblock_man *mblock);
|
||||
|
||||
#define fast_mblock_total_count(mblock) (mblock)->total_count
|
||||
#define fast_mblock_total_count(mblock) (mblock)->info.element_total_count
|
||||
|
||||
/**
|
||||
init mblock manager
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_mpool.c
|
||||
|
||||
#include <errno.h>
|
||||
|
|
@ -33,6 +48,10 @@ int fast_mpool_init(struct fast_mpool_man *mpool,
|
|||
|
||||
mpool->malloc_chain_head = NULL;
|
||||
mpool->free_chain_head = NULL;
|
||||
mpool->alloc_count = 0;
|
||||
mpool->alloc_bytes = 0;
|
||||
mpool->reset.count = 0;
|
||||
mpool->reset.last_alloc_count = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -44,14 +63,10 @@ static int fast_mpool_prealloc(struct fast_mpool_man *mpool,
|
|||
int bytes;
|
||||
|
||||
bytes = sizeof(struct fast_mpool_malloc) + alloc_size;
|
||||
pMallocNode = (struct fast_mpool_malloc *)malloc(bytes);
|
||||
pMallocNode = (struct fast_mpool_malloc *)fc_malloc(bytes);
|
||||
if (pMallocNode == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, bytes, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
pMallocNode->alloc_size = alloc_size;
|
||||
|
|
@ -127,6 +142,8 @@ static inline void *fast_mpool_do_alloc(struct fast_mpool_man *mpool,
|
|||
fast_mpool_remove_free_node(mpool, pMallocNode);
|
||||
}
|
||||
|
||||
mpool->alloc_count++;
|
||||
mpool->alloc_bytes += size;
|
||||
return ptr;
|
||||
}
|
||||
return NULL;
|
||||
|
|
@ -165,42 +182,50 @@ void *fast_mpool_alloc(struct fast_mpool_man *mpool, const int size)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int fast_mpool_strdup_ex(struct fast_mpool_man *mpool, string_t *dest,
|
||||
const char *src, const int len)
|
||||
void *fast_mpool_memdup(struct fast_mpool_man *mpool,
|
||||
const void *src, const int len)
|
||||
{
|
||||
dest->str = (char *)fast_mpool_alloc(mpool, len);
|
||||
if (dest->str == NULL)
|
||||
void *dest;
|
||||
dest = (char *)fast_mpool_alloc(mpool, len);
|
||||
if (dest == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"alloc %d bytes from mpool fail", __LINE__, len);
|
||||
return ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(dest->str, src, len);
|
||||
memcpy(dest, src, len);
|
||||
}
|
||||
dest->len = len;
|
||||
return 0;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void fast_mpool_reset(struct fast_mpool_man *mpool)
|
||||
{
|
||||
struct fast_mpool_malloc *pMallocNode;
|
||||
struct fast_mpool_malloc *pMallocNode;
|
||||
|
||||
mpool->free_chain_head = NULL;
|
||||
pMallocNode = mpool->malloc_chain_head;
|
||||
while (pMallocNode != NULL)
|
||||
{
|
||||
mpool->reset.count++;
|
||||
if (mpool->reset.last_alloc_count == mpool->alloc_count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mpool->reset.last_alloc_count = mpool->alloc_count;
|
||||
mpool->free_chain_head = NULL;
|
||||
pMallocNode = mpool->malloc_chain_head;
|
||||
while (pMallocNode != NULL)
|
||||
{
|
||||
pMallocNode->free_ptr = pMallocNode->base_ptr;
|
||||
|
||||
pMallocNode->free_next = mpool->free_chain_head;
|
||||
mpool->free_chain_head = pMallocNode;
|
||||
|
||||
pMallocNode = pMallocNode->malloc_next;
|
||||
}
|
||||
pMallocNode = pMallocNode->malloc_next;
|
||||
}
|
||||
}
|
||||
|
||||
void fast_mpool_stats(struct fast_mpool_man *mpool, struct fast_mpool_stats *stats)
|
||||
void fast_mpool_stats(struct fast_mpool_man *mpool,
|
||||
struct fast_mpool_stats *stats)
|
||||
{
|
||||
struct fast_mpool_malloc *pMallocNode;
|
||||
|
||||
|
|
@ -213,7 +238,8 @@ void fast_mpool_stats(struct fast_mpool_man *mpool, struct fast_mpool_stats *sta
|
|||
while (pMallocNode != NULL)
|
||||
{
|
||||
stats->total_bytes += pMallocNode->alloc_size;
|
||||
stats->free_bytes += (int)(pMallocNode->end_ptr - pMallocNode->free_ptr);
|
||||
stats->free_bytes += (int)(pMallocNode->end_ptr -
|
||||
pMallocNode->free_ptr);
|
||||
stats->total_trunk_count++;
|
||||
|
||||
pMallocNode = pMallocNode->malloc_next;
|
||||
|
|
@ -227,3 +253,25 @@ void fast_mpool_stats(struct fast_mpool_man *mpool, struct fast_mpool_stats *sta
|
|||
}
|
||||
}
|
||||
|
||||
void fast_mpool_log_stats(struct fast_mpool_man *mpool)
|
||||
{
|
||||
struct fast_mpool_stats stats;
|
||||
char sz_total_bytes[32];
|
||||
char sz_free_bytes[32];
|
||||
char sz_alloc_bytes[32];
|
||||
|
||||
fast_mpool_stats(mpool, &stats);
|
||||
|
||||
long_to_comma_str(stats.total_bytes, sz_total_bytes);
|
||||
long_to_comma_str(stats.free_bytes, sz_free_bytes);
|
||||
long_to_comma_str(mpool->alloc_bytes, sz_alloc_bytes);
|
||||
logInfo("alloc_size_once: %d, discard_size: %d, "
|
||||
"bytes: {total: %s, free: %s}, "
|
||||
"trunk_count: {total: %d, free: %d}, "
|
||||
"alloc_count: %"PRId64", alloc_bytes: %s, "
|
||||
"reset_count: %"PRId64, mpool->alloc_size_once,
|
||||
mpool->discard_size, sz_total_bytes,
|
||||
sz_free_bytes, stats.total_trunk_count,
|
||||
stats.free_trunk_count, mpool->alloc_count,
|
||||
sz_alloc_bytes, mpool->reset.count);
|
||||
}
|
||||
|
|
|
|||
128
src/fast_mpool.h
128
src/fast_mpool.h
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_mpool.h
|
||||
|
||||
|
|
@ -30,10 +37,16 @@ struct fast_mpool_malloc
|
|||
|
||||
struct fast_mpool_man
|
||||
{
|
||||
struct fast_mpool_malloc *malloc_chain_head; //malloc chain to be freed
|
||||
struct fast_mpool_malloc *free_chain_head; //free node chain
|
||||
int alloc_size_once; //alloc size once, default: 1MB
|
||||
int discard_size; //discard size, default: 64 bytes
|
||||
struct fast_mpool_malloc *malloc_chain_head; //malloc chain to be freed
|
||||
struct fast_mpool_malloc *free_chain_head; //free node chain
|
||||
int alloc_size_once; //alloc size once, default: 1MB
|
||||
int discard_size; //discard size, default: 64 bytes
|
||||
int64_t alloc_count;
|
||||
int64_t alloc_bytes;
|
||||
struct {
|
||||
int64_t count;
|
||||
int64_t last_alloc_count;
|
||||
} reset;
|
||||
};
|
||||
|
||||
struct fast_mpool_stats
|
||||
|
|
@ -83,6 +96,46 @@ return the alloced ptr, return NULL if fail
|
|||
void *fast_mpool_alloc(struct fast_mpool_man *mpool, const int size);
|
||||
|
||||
|
||||
/**
|
||||
alloc and copy memory from the mpool
|
||||
parameters:
|
||||
mpool: the mpool pointer
|
||||
src: the source memory pointer
|
||||
len: the length of the source memory
|
||||
return alloc and duplicate memory pointer, NULL for fail
|
||||
*/
|
||||
void *fast_mpool_memdup(struct fast_mpool_man *mpool,
|
||||
const void *src, const int len);
|
||||
|
||||
|
||||
/**
|
||||
alloc and copy string from the mpool
|
||||
parameters:
|
||||
mpool: the mpool pointer
|
||||
src: the source '\0' terminated string
|
||||
len: the length of the source string
|
||||
return alloc and duplicate string pointer, NULL for fail
|
||||
*/
|
||||
static inline char *fast_mpool_strdup_ex(struct fast_mpool_man *mpool,
|
||||
const char *src, const int len)
|
||||
{
|
||||
return (char *)fast_mpool_memdup(mpool, src, len + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
alloc and copy string from the mpool
|
||||
parameters:
|
||||
mpool: the mpool pointer
|
||||
src: the source '\0' terminated string
|
||||
len: the length of the source string
|
||||
return alloc and duplicate string pointer, NULL for fail
|
||||
*/
|
||||
static inline char *fast_mpool_strdup(struct fast_mpool_man *mpool,
|
||||
const char *src)
|
||||
{
|
||||
return (char *)fast_mpool_memdup(mpool, src, strlen(src) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
alloc and copy string from the mpool
|
||||
parameters:
|
||||
|
|
@ -92,23 +145,12 @@ parameters:
|
|||
len: the length of the source string
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_mpool_strdup_ex(struct fast_mpool_man *mpool, string_t *dest,
|
||||
const char *src, const int len);
|
||||
|
||||
/**
|
||||
alloc and copy string from the mpool
|
||||
parameters:
|
||||
mpool: the mpool pointer
|
||||
dest: the dest string (return the alloced memory in dest->str)
|
||||
src: the source string
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
static inline int fast_mpool_strdup(struct fast_mpool_man *mpool,
|
||||
string_t *dest, const char *src)
|
||||
static inline int fast_mpool_alloc_string_ex(struct fast_mpool_man *mpool,
|
||||
string_t *dest, const char *src, const int len)
|
||||
{
|
||||
int len;
|
||||
len = (src != NULL) ? strlen(src) : 0;
|
||||
return fast_mpool_strdup_ex(mpool, dest, src, len);
|
||||
dest->str = (char *)fast_mpool_memdup(mpool, src, len);
|
||||
dest->len = len;
|
||||
return dest->str != NULL ? 0 : ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -119,10 +161,26 @@ parameters:
|
|||
src: the source string
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
static inline int fast_mpool_strdup2(struct fast_mpool_man *mpool,
|
||||
static inline int fast_mpool_alloc_string(struct fast_mpool_man *mpool,
|
||||
string_t *dest, const char *src)
|
||||
{
|
||||
int len;
|
||||
len = (src != NULL) ? strlen(src) : 0;
|
||||
return fast_mpool_alloc_string_ex(mpool, dest, src, len);
|
||||
}
|
||||
|
||||
/**
|
||||
alloc and copy string from the mpool
|
||||
parameters:
|
||||
mpool: the mpool pointer
|
||||
dest: the dest string (return the alloced memory in dest->str)
|
||||
src: the source string
|
||||
return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
static inline int fast_mpool_alloc_string_ex2(struct fast_mpool_man *mpool,
|
||||
string_t *dest, const string_t *src)
|
||||
{
|
||||
return fast_mpool_strdup_ex(mpool, dest, src->str, src->len);
|
||||
return fast_mpool_alloc_string_ex(mpool, dest, src->str, src->len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -130,8 +188,18 @@ get stats
|
|||
parameters:
|
||||
mpool: the mpool pointer
|
||||
stats: return the stats
|
||||
return none
|
||||
*/
|
||||
void fast_mpool_stats(struct fast_mpool_man *mpool, struct fast_mpool_stats *stats);
|
||||
void fast_mpool_stats(struct fast_mpool_man *mpool,
|
||||
struct fast_mpool_stats *stats);
|
||||
|
||||
/**
|
||||
log stats info
|
||||
parameters:
|
||||
mpool: the mpool pointer
|
||||
return none
|
||||
*/
|
||||
void fast_mpool_log_stats(struct fast_mpool_man *mpool);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,676 +1,238 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_task_queue.c
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/resource.h>
|
||||
#include <pthread.h>
|
||||
#include <inttypes.h>
|
||||
#include "fast_task_queue.h"
|
||||
#include "logger.h"
|
||||
#include "shared_func.h"
|
||||
#include "pthread_func.h"
|
||||
#include "fc_memory.h"
|
||||
#include "fast_task_queue.h"
|
||||
|
||||
static struct fast_task_queue g_free_queue;
|
||||
|
||||
struct mpool_node {
|
||||
struct fast_task_info *blocks;
|
||||
struct fast_task_info *last_block; //last block
|
||||
struct mpool_node *next;
|
||||
};
|
||||
|
||||
struct mpool_chain {
|
||||
struct mpool_node *head;
|
||||
struct mpool_node *tail;
|
||||
};
|
||||
|
||||
static struct mpool_chain g_mpool = {NULL, NULL};
|
||||
|
||||
int task_queue_init(struct fast_task_queue *pQueue)
|
||||
static int task_alloc_init(struct fast_task_info *task,
|
||||
struct fast_task_queue *queue)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=init_pthread_lock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"init_pthread_lock fail, errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
pQueue->head = NULL;
|
||||
pQueue->tail = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mpool_node *malloc_mpool(const int total_alloc_size)
|
||||
{
|
||||
struct fast_task_info *pTask;
|
||||
char *p;
|
||||
char *pCharEnd;
|
||||
struct mpool_node *mpool;
|
||||
|
||||
mpool = (struct mpool_node *)malloc(sizeof(struct mpool_node));
|
||||
if (mpool == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, (int)sizeof(struct mpool_node), \
|
||||
errno, STRERROR(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mpool->next = NULL;
|
||||
mpool->blocks = (struct fast_task_info *)malloc(total_alloc_size);
|
||||
if (mpool->blocks == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, total_alloc_size, \
|
||||
errno, STRERROR(errno));
|
||||
free(mpool);
|
||||
return NULL;
|
||||
}
|
||||
memset(mpool->blocks, 0, total_alloc_size);
|
||||
|
||||
pCharEnd = ((char *)mpool->blocks) + total_alloc_size;
|
||||
for (p=(char *)mpool->blocks; p<pCharEnd; p += g_free_queue.block_size)
|
||||
{
|
||||
pTask = (struct fast_task_info *)p;
|
||||
pTask->size = g_free_queue.min_buff_size;
|
||||
|
||||
pTask->arg = p + ALIGNED_TASK_INFO_SIZE;
|
||||
if (g_free_queue.malloc_whole_block)
|
||||
{
|
||||
pTask->data = (char *)pTask->arg + \
|
||||
g_free_queue.arg_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
pTask->data = (char *)malloc(pTask->size);
|
||||
if (pTask->data == NULL)
|
||||
{
|
||||
char *pt;
|
||||
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, pTask->size, \
|
||||
errno, STRERROR(errno));
|
||||
|
||||
for (pt=(char *)mpool->blocks; pt < p; \
|
||||
pt += g_free_queue.block_size)
|
||||
{
|
||||
free(((struct fast_task_info *)pt)->data);
|
||||
}
|
||||
|
||||
free(mpool->blocks);
|
||||
free(mpool);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mpool->last_block = (struct fast_task_info *)(pCharEnd - g_free_queue.block_size);
|
||||
for (p=(char *)mpool->blocks; p<(char *)mpool->last_block; p += g_free_queue.block_size)
|
||||
{
|
||||
pTask = (struct fast_task_info *)p;
|
||||
pTask->next = (struct fast_task_info *)(p + g_free_queue.block_size);
|
||||
}
|
||||
mpool->last_block->next = NULL;
|
||||
|
||||
return mpool;
|
||||
}
|
||||
|
||||
int free_queue_init_ex(const int max_connections, const int init_connections,
|
||||
const int alloc_task_once, const int min_buff_size,
|
||||
const int max_buff_size, const int arg_size)
|
||||
{
|
||||
#define MAX_DATA_SIZE (256 * 1024 * 1024)
|
||||
int64_t total_size;
|
||||
struct mpool_node *mpool;
|
||||
int alloc_size;
|
||||
int alloc_once;
|
||||
int result;
|
||||
int loop_count;
|
||||
int aligned_min_size;
|
||||
int aligned_max_size;
|
||||
int aligned_arg_size;
|
||||
rlim_t max_data_size;
|
||||
|
||||
if ((result=init_pthread_lock(&(g_free_queue.lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"init_pthread_lock fail, errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
aligned_min_size = MEM_ALIGN(min_buff_size);
|
||||
aligned_max_size = MEM_ALIGN(max_buff_size);
|
||||
aligned_arg_size = MEM_ALIGN(arg_size);
|
||||
g_free_queue.block_size = ALIGNED_TASK_INFO_SIZE + aligned_arg_size;
|
||||
alloc_size = g_free_queue.block_size * init_connections;
|
||||
if (aligned_max_size > aligned_min_size)
|
||||
{
|
||||
total_size = alloc_size;
|
||||
g_free_queue.malloc_whole_block = false;
|
||||
max_data_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct rlimit rlimit_data;
|
||||
|
||||
if (getrlimit(RLIMIT_DATA, &rlimit_data) < 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call getrlimit fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : EPERM;
|
||||
}
|
||||
if (rlimit_data.rlim_cur == RLIM_INFINITY)
|
||||
{
|
||||
max_data_size = MAX_DATA_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
max_data_size = rlimit_data.rlim_cur;
|
||||
if (max_data_size > MAX_DATA_SIZE)
|
||||
{
|
||||
max_data_size = MAX_DATA_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
if (max_data_size >= (int64_t)(g_free_queue.block_size + aligned_min_size) *
|
||||
(int64_t)init_connections)
|
||||
{
|
||||
total_size = alloc_size + (int64_t)aligned_min_size *
|
||||
init_connections;
|
||||
g_free_queue.malloc_whole_block = true;
|
||||
g_free_queue.block_size += aligned_min_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
total_size = alloc_size;
|
||||
g_free_queue.malloc_whole_block = false;
|
||||
max_data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
g_free_queue.max_connections = max_connections;
|
||||
g_free_queue.alloc_connections = init_connections;
|
||||
if (alloc_task_once <= 0)
|
||||
{
|
||||
g_free_queue.alloc_task_once = 256;
|
||||
alloc_once = MAX_DATA_SIZE / g_free_queue.block_size;
|
||||
if (g_free_queue.alloc_task_once > alloc_once)
|
||||
{
|
||||
g_free_queue.alloc_task_once = alloc_once > 0 ? alloc_once : 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_free_queue.alloc_task_once = alloc_task_once;
|
||||
}
|
||||
g_free_queue.min_buff_size = aligned_min_size;
|
||||
g_free_queue.max_buff_size = aligned_max_size;
|
||||
g_free_queue.arg_size = aligned_arg_size;
|
||||
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"max_connections: %d, init_connections: %d, alloc_task_once: %d, "
|
||||
"min_buff_size: %d, max_buff_size: %d, block_size: %d, "
|
||||
"arg_size: %d, max_data_size: %d, total_size: %"PRId64,
|
||||
__LINE__, max_connections, init_connections,
|
||||
g_free_queue.alloc_task_once, aligned_min_size, aligned_max_size,
|
||||
g_free_queue.block_size, aligned_arg_size, (int)max_data_size, total_size);
|
||||
|
||||
if ((!g_free_queue.malloc_whole_block) || (total_size <= max_data_size))
|
||||
{
|
||||
loop_count = 1;
|
||||
mpool = malloc_mpool(total_size);
|
||||
if (mpool == NULL)
|
||||
{
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
g_mpool.head = mpool;
|
||||
g_mpool.tail = mpool;
|
||||
}
|
||||
else
|
||||
{
|
||||
int remain_count;
|
||||
int alloc_count;
|
||||
int current_alloc_size;
|
||||
|
||||
loop_count = 0;
|
||||
remain_count = init_connections;
|
||||
alloc_once = max_data_size / g_free_queue.block_size;
|
||||
while (remain_count > 0)
|
||||
{
|
||||
alloc_count = (remain_count > alloc_once) ?
|
||||
alloc_once : remain_count;
|
||||
current_alloc_size = g_free_queue.block_size * alloc_count;
|
||||
mpool = malloc_mpool(current_alloc_size);
|
||||
if (mpool == NULL)
|
||||
{
|
||||
free_queue_destroy();
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
|
||||
if (g_mpool.tail == NULL)
|
||||
{
|
||||
g_mpool.head = mpool;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_mpool.tail->next = mpool;
|
||||
g_mpool.tail->last_block->next = mpool->blocks; //link previous mpool to current
|
||||
}
|
||||
g_mpool.tail = mpool;
|
||||
|
||||
remain_count -= alloc_count;
|
||||
loop_count++;
|
||||
}
|
||||
|
||||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"alloc_once: %d", __LINE__, alloc_once);
|
||||
}
|
||||
|
||||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"malloc task info as whole: %d, malloc loop count: %d", \
|
||||
__LINE__, g_free_queue.malloc_whole_block, loop_count);
|
||||
|
||||
if (g_mpool.head != NULL)
|
||||
{
|
||||
g_free_queue.head = g_mpool.head->blocks;
|
||||
g_free_queue.tail = g_mpool.tail->last_block;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int free_queue_init(const int max_connections, const int min_buff_size,
|
||||
const int max_buff_size, const int arg_size)
|
||||
{
|
||||
return free_queue_init_ex(max_connections, max_connections,
|
||||
0, min_buff_size, max_buff_size, arg_size);
|
||||
}
|
||||
|
||||
void free_queue_destroy()
|
||||
{
|
||||
struct mpool_node *mpool;
|
||||
struct mpool_node *mp;
|
||||
|
||||
if (g_mpool.head == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_free_queue.malloc_whole_block)
|
||||
{
|
||||
char *p;
|
||||
char *pCharEnd;
|
||||
struct fast_task_info *pTask;
|
||||
|
||||
mpool = g_mpool.head;
|
||||
while (mpool != NULL)
|
||||
{
|
||||
pCharEnd = (char *)mpool->last_block + g_free_queue.block_size;
|
||||
for (p=(char *)mpool->blocks; p<pCharEnd; p += g_free_queue.block_size)
|
||||
{
|
||||
pTask = (struct fast_task_info *)p;
|
||||
if (pTask->data != NULL)
|
||||
{
|
||||
free(pTask->data);
|
||||
pTask->data = NULL;
|
||||
}
|
||||
}
|
||||
mpool = mpool->next;
|
||||
}
|
||||
}
|
||||
|
||||
mpool = g_mpool.head;
|
||||
while (mpool != NULL)
|
||||
{
|
||||
mp = mpool;
|
||||
mpool = mpool->next;
|
||||
|
||||
free(mp->blocks);
|
||||
free(mp);
|
||||
}
|
||||
g_mpool.head = g_mpool.tail = NULL;
|
||||
|
||||
pthread_mutex_destroy(&(g_free_queue.lock));
|
||||
}
|
||||
|
||||
static int free_queue_realloc()
|
||||
{
|
||||
struct mpool_node *mpool;
|
||||
struct fast_task_info *head;
|
||||
struct fast_task_info *tail;
|
||||
int remain_count;
|
||||
int alloc_count;
|
||||
int current_alloc_size;
|
||||
|
||||
head = tail = NULL;
|
||||
remain_count = g_free_queue.max_connections -
|
||||
g_free_queue.alloc_connections;
|
||||
alloc_count = (remain_count > g_free_queue.alloc_task_once) ?
|
||||
g_free_queue.alloc_task_once : remain_count;
|
||||
if (alloc_count > 0)
|
||||
{
|
||||
current_alloc_size = g_free_queue.block_size * alloc_count;
|
||||
mpool = malloc_mpool(current_alloc_size);
|
||||
if (mpool == NULL)
|
||||
{
|
||||
task->arg = (char *)task + ALIGNED_TASK_INFO_SIZE + queue->padding_size;
|
||||
task->send.ptr = &task->send.holder;
|
||||
task->send.ptr->size = queue->min_buff_size;
|
||||
if (queue->malloc_whole_block) {
|
||||
task->send.ptr->data = (char *)task->arg + queue->arg_size;
|
||||
} else {
|
||||
task->send.ptr->data = (char *)fc_malloc(task->send.ptr->size);
|
||||
if (task->send.ptr->data == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (g_mpool.tail == NULL)
|
||||
{
|
||||
g_mpool.head = mpool;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_mpool.tail->next = mpool;
|
||||
}
|
||||
g_mpool.tail = mpool;
|
||||
|
||||
head = mpool->blocks;
|
||||
tail = mpool->last_block;
|
||||
|
||||
remain_count -= alloc_count;
|
||||
}
|
||||
else {
|
||||
return ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_free_queue.head == NULL)
|
||||
{
|
||||
g_free_queue.head = head;
|
||||
if (queue->double_buffers) {
|
||||
task->recv.ptr = &task->recv.holder;
|
||||
task->recv.ptr->size = queue->min_buff_size;
|
||||
task->recv.ptr->data = (char *)fc_malloc(task->recv.ptr->size);
|
||||
if (task->recv.ptr->data == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
} else {
|
||||
task->recv.ptr = &task->send.holder;
|
||||
}
|
||||
if (g_free_queue.tail != NULL)
|
||||
{
|
||||
g_free_queue.tail->next = head;
|
||||
|
||||
task->free_queue = queue;
|
||||
if (queue->init_callback != NULL) {
|
||||
return queue->init_callback(task, queue->init_arg);
|
||||
}
|
||||
g_free_queue.tail = tail;
|
||||
|
||||
g_free_queue.alloc_connections += alloc_count;
|
||||
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"alloc_connections: %d, realloc %d elements", __LINE__,
|
||||
g_free_queue.alloc_connections, alloc_count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct fast_task_info *free_queue_pop()
|
||||
int free_queue_init_ex2(struct fast_task_queue *queue, const char *name,
|
||||
const bool double_buffers, const bool need_shrink,
|
||||
const int max_connections, const int alloc_task_once,
|
||||
const int min_buff_size, const int max_buff_size,
|
||||
const int padding_size, const int arg_size,
|
||||
TaskInitCallback init_callback, void *init_arg)
|
||||
{
|
||||
struct fast_task_info *pTask;
|
||||
int i;
|
||||
#define MAX_DATA_SIZE (256 * 1024 * 1024)
|
||||
int alloc_once;
|
||||
int aligned_min_size;
|
||||
int aligned_max_size;
|
||||
int aligned_padding_size;
|
||||
int aligned_arg_size;
|
||||
rlim_t max_data_size;
|
||||
char aname[64];
|
||||
|
||||
if ((pTask=task_queue_pop(&g_free_queue)) != NULL)
|
||||
{
|
||||
return pTask;
|
||||
aligned_min_size = MEM_ALIGN(min_buff_size);
|
||||
aligned_max_size = MEM_ALIGN(max_buff_size);
|
||||
aligned_padding_size = MEM_ALIGN(padding_size);
|
||||
aligned_arg_size = MEM_ALIGN(arg_size);
|
||||
queue->block_size = ALIGNED_TASK_INFO_SIZE +
|
||||
aligned_padding_size + aligned_arg_size;
|
||||
if (alloc_task_once <= 0) {
|
||||
alloc_once = FC_MIN(MAX_DATA_SIZE / queue->block_size, 256);
|
||||
if (alloc_once == 0) {
|
||||
alloc_once = 1;
|
||||
}
|
||||
} else {
|
||||
alloc_once = alloc_task_once;
|
||||
}
|
||||
|
||||
if (g_free_queue.alloc_connections >= g_free_queue.max_connections)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
if (aligned_max_size > aligned_min_size) {
|
||||
queue->malloc_whole_block = false;
|
||||
max_data_size = 0;
|
||||
} else {
|
||||
struct rlimit rlimit_data;
|
||||
|
||||
for (i=0; i<10; i++)
|
||||
{
|
||||
pthread_mutex_lock(&g_free_queue.lock);
|
||||
if (g_free_queue.alloc_connections >= g_free_queue.max_connections)
|
||||
{
|
||||
if (g_free_queue.head == NULL)
|
||||
{
|
||||
pthread_mutex_unlock(&g_free_queue.lock);
|
||||
return NULL;
|
||||
if (getrlimit(RLIMIT_DATA, &rlimit_data) < 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call getrlimit fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : EPERM;
|
||||
}
|
||||
if (rlimit_data.rlim_cur == RLIM_INFINITY) {
|
||||
max_data_size = MAX_DATA_SIZE;
|
||||
} else {
|
||||
max_data_size = rlimit_data.rlim_cur;
|
||||
if (max_data_size > MAX_DATA_SIZE) {
|
||||
max_data_size = MAX_DATA_SIZE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_free_queue.head == NULL && free_queue_realloc() != 0)
|
||||
{
|
||||
pthread_mutex_unlock(&g_free_queue.lock);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&g_free_queue.lock);
|
||||
|
||||
if ((pTask=task_queue_pop(&g_free_queue)) != NULL)
|
||||
if (max_data_size >= (int64_t)(queue->block_size +
|
||||
aligned_min_size) * (int64_t)alloc_once)
|
||||
{
|
||||
return pTask;
|
||||
queue->malloc_whole_block = true;
|
||||
queue->block_size += aligned_min_size;
|
||||
} else {
|
||||
queue->malloc_whole_block = false;
|
||||
max_data_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
queue->double_buffers = double_buffers;
|
||||
queue->need_shrink = need_shrink;
|
||||
queue->min_buff_size = aligned_min_size;
|
||||
queue->max_buff_size = aligned_max_size;
|
||||
queue->padding_size = aligned_padding_size;
|
||||
queue->arg_size = aligned_arg_size;
|
||||
queue->init_callback = init_callback;
|
||||
queue->init_arg = init_arg;
|
||||
queue->release_callback = NULL;
|
||||
|
||||
/*
|
||||
logInfo("file: "__FILE__", line: %d, [%s] double_buffers: %d, "
|
||||
"max_connections: %d, alloc_once: %d, malloc_whole_block: %d, "
|
||||
"min_buff_size: %d, max_buff_size: %d, block_size: %d, "
|
||||
"padding_size: %d, arg_size: %d, max_data_size: %"PRId64,
|
||||
__LINE__, name, double_buffers, max_connections, alloc_once,
|
||||
queue->malloc_whole_block, aligned_min_size, aligned_max_size,
|
||||
queue->block_size, aligned_padding_size, aligned_arg_size,
|
||||
(int64_t)max_data_size);
|
||||
*/
|
||||
|
||||
fc_combine_two_strings(name, "task", '-', aname);
|
||||
return fast_mblock_init_ex1(&queue->allocator, aname,
|
||||
queue->block_size, alloc_once, max_connections,
|
||||
(fast_mblock_object_init_func)task_alloc_init,
|
||||
queue, true);
|
||||
}
|
||||
|
||||
static int _realloc_buffer(struct fast_task_info *pTask, const int new_size,
|
||||
const bool copy_data)
|
||||
void free_queue_destroy(struct fast_task_queue *queue)
|
||||
{
|
||||
fast_mblock_destroy(&queue->allocator);
|
||||
}
|
||||
|
||||
static int _realloc_buffer(struct fast_net_buffer *buffer,
|
||||
const int new_size, const bool copy_data)
|
||||
{
|
||||
char *new_buff;
|
||||
new_buff = (char *)malloc(new_size);
|
||||
if (new_buff == NULL)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, new_size,
|
||||
errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
|
||||
new_buff = (char *)fc_malloc(new_size);
|
||||
if (new_buff == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (copy_data && pTask->offset > 0) {
|
||||
memcpy(new_buff, pTask->data, pTask->offset);
|
||||
|
||||
if (copy_data && buffer->offset > 0) {
|
||||
memcpy(new_buff, buffer->data, buffer->offset);
|
||||
}
|
||||
free(buffer->data);
|
||||
buffer->size = new_size;
|
||||
buffer->data = new_buff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void free_queue_push(struct fast_task_info *task)
|
||||
{
|
||||
if (task->free_queue->release_callback != NULL) {
|
||||
task->free_queue->release_callback(task);
|
||||
}
|
||||
|
||||
*(task->client_ip) = '\0';
|
||||
task->send.ptr->length = 0;
|
||||
task->send.ptr->offset = 0;
|
||||
task->req_count = 0;
|
||||
if (task->free_queue->need_shrink && task->send.
|
||||
ptr->size > task->free_queue->min_buff_size)
|
||||
{ //need thrink
|
||||
_realloc_buffer(task->send.ptr, task->free_queue->
|
||||
min_buff_size, false);
|
||||
task->shrinked = true;
|
||||
}
|
||||
|
||||
if (task->free_queue->double_buffers) {
|
||||
task->recv.ptr->length = 0;
|
||||
task->recv.ptr->offset = 0;
|
||||
if (task->free_queue->need_shrink && task->recv.
|
||||
ptr->size > task->free_queue->min_buff_size)
|
||||
{
|
||||
_realloc_buffer(task->recv.ptr, task->free_queue->
|
||||
min_buff_size, false);
|
||||
task->shrinked = true;
|
||||
}
|
||||
free(pTask->data);
|
||||
pTask->size = new_size;
|
||||
pTask->data = new_buff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
fast_mblock_free_object(&task->free_queue->allocator, task);
|
||||
}
|
||||
|
||||
int free_queue_push(struct fast_task_info *pTask)
|
||||
{
|
||||
int result;
|
||||
|
||||
*(pTask->client_ip) = '\0';
|
||||
pTask->length = 0;
|
||||
pTask->offset = 0;
|
||||
pTask->req_count = 0;
|
||||
|
||||
if (pTask->size > g_free_queue.min_buff_size) //need thrink
|
||||
{
|
||||
_realloc_buffer(pTask, g_free_queue.min_buff_size, false);
|
||||
}
|
||||
|
||||
if ((result=pthread_mutex_lock(&g_free_queue.lock)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
pTask->next = g_free_queue.head;
|
||||
g_free_queue.head = pTask;
|
||||
if (g_free_queue.tail == NULL)
|
||||
{
|
||||
g_free_queue.tail = pTask;
|
||||
}
|
||||
|
||||
if ((result=pthread_mutex_unlock(&g_free_queue.lock)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int free_queue_count()
|
||||
{
|
||||
return task_queue_count(&g_free_queue);
|
||||
}
|
||||
|
||||
int free_queue_alloc_connections()
|
||||
{
|
||||
return g_free_queue.alloc_connections;
|
||||
}
|
||||
|
||||
int free_queue_set_buffer_size(struct fast_task_info *pTask,
|
||||
const int expect_size)
|
||||
{
|
||||
return task_queue_set_buffer_size(&g_free_queue, pTask, expect_size);
|
||||
}
|
||||
|
||||
int free_queue_realloc_buffer(struct fast_task_info *pTask,
|
||||
const int expect_size)
|
||||
{
|
||||
return task_queue_realloc_buffer(&g_free_queue, pTask, expect_size);
|
||||
}
|
||||
|
||||
int task_queue_push(struct fast_task_queue *pQueue, \
|
||||
struct fast_task_info *pTask)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
pTask->next = NULL;
|
||||
if (pQueue->tail == NULL)
|
||||
{
|
||||
pQueue->head = pTask;
|
||||
}
|
||||
else
|
||||
{
|
||||
pQueue->tail->next = pTask;
|
||||
}
|
||||
pQueue->tail = pTask;
|
||||
|
||||
if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct fast_task_info *task_queue_pop(struct fast_task_queue *pQueue)
|
||||
{
|
||||
struct fast_task_info *pTask;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pTask = pQueue->head;
|
||||
if (pTask != NULL)
|
||||
{
|
||||
pQueue->head = pTask->next;
|
||||
if (pQueue->head == NULL)
|
||||
{
|
||||
pQueue->tail = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
return pTask;
|
||||
}
|
||||
|
||||
int task_queue_count(struct fast_task_queue *pQueue)
|
||||
{
|
||||
struct fast_task_info *pTask;
|
||||
int count;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutex_lock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return 0;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
pTask = pQueue->head;
|
||||
while (pTask != NULL)
|
||||
{
|
||||
pTask = pTask->next;
|
||||
count++;
|
||||
}
|
||||
|
||||
if ((result=pthread_mutex_unlock(&(pQueue->lock))) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int task_queue_get_new_buffer_size(const int min_buff_size,
|
||||
int free_queue_get_new_buffer_size(const int min_buff_size,
|
||||
const int max_buff_size, const int expect_size, int *new_size)
|
||||
{
|
||||
if (min_buff_size == max_buff_size)
|
||||
{
|
||||
if (min_buff_size == max_buff_size) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"can't change buffer size because NOT supported", __LINE__);
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (expect_size > max_buff_size)
|
||||
{
|
||||
if (expect_size > max_buff_size) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"can't change buffer size because expect buffer size: %d "
|
||||
"exceeds max buffer size: %d", __LINE__, expect_size,
|
||||
max_buff_size);
|
||||
return EOVERFLOW;
|
||||
} else if (expect_size == max_buff_size) {
|
||||
*new_size = max_buff_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*new_size = min_buff_size;
|
||||
if (expect_size > min_buff_size)
|
||||
{
|
||||
while (*new_size < expect_size)
|
||||
{
|
||||
if (expect_size > min_buff_size) {
|
||||
while (*new_size < expect_size) {
|
||||
*new_size *= 2;
|
||||
}
|
||||
if (*new_size > max_buff_size)
|
||||
{
|
||||
if (*new_size > max_buff_size) {
|
||||
*new_size = max_buff_size;
|
||||
}
|
||||
}
|
||||
|
|
@ -678,42 +240,43 @@ int task_queue_get_new_buffer_size(const int min_buff_size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define _get_new_buffer_size(pQueue, expect_size, new_size) \
|
||||
task_queue_get_new_buffer_size(pQueue->min_buff_size, \
|
||||
pQueue->max_buff_size, expect_size, new_size)
|
||||
#define _get_new_buffer_size(queue, expect_size, new_size) \
|
||||
free_queue_get_new_buffer_size(queue->min_buff_size, \
|
||||
queue->max_buff_size, expect_size, new_size)
|
||||
|
||||
int task_queue_set_buffer_size(struct fast_task_queue *pQueue,
|
||||
struct fast_task_info *pTask, const int expect_size)
|
||||
int free_queue_set_buffer_size(struct fast_task_info *task,
|
||||
struct fast_net_buffer *buffer, const int expect_size)
|
||||
{
|
||||
int result;
|
||||
int new_size;
|
||||
|
||||
if ((result=_get_new_buffer_size(pQueue, expect_size, &new_size)) != 0) {
|
||||
if ((result=_get_new_buffer_size(task->free_queue,
|
||||
expect_size, &new_size)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if (pTask->size == new_size) //do NOT need change buffer size
|
||||
{
|
||||
if (buffer->size == new_size) { //do NOT need change buffer size
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _realloc_buffer(pTask, new_size, false);
|
||||
return _realloc_buffer(buffer, new_size, false);
|
||||
}
|
||||
|
||||
int task_queue_realloc_buffer(struct fast_task_queue *pQueue,
|
||||
struct fast_task_info *pTask, const int expect_size)
|
||||
int free_queue_realloc_buffer(struct fast_task_info *task,
|
||||
struct fast_net_buffer *buffer, const int expect_size)
|
||||
{
|
||||
int result;
|
||||
int new_size;
|
||||
|
||||
if (pTask->size >= expect_size) //do NOT need change buffer size
|
||||
{
|
||||
if (buffer->size >= expect_size) { //do NOT need change buffer size
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((result=_get_new_buffer_size(pQueue, expect_size, &new_size)) != 0) {
|
||||
if ((result=_get_new_buffer_size(task->free_queue,
|
||||
expect_size, &new_size)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return _realloc_buffer(pTask, new_size, true);
|
||||
return _realloc_buffer(buffer, new_size, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fast_task_queue.h
|
||||
|
||||
|
|
@ -16,8 +23,13 @@
|
|||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "common_define.h"
|
||||
#include "fc_list.h"
|
||||
#include "ioevent.h"
|
||||
#include "fast_timer.h"
|
||||
#include "fast_mblock.h"
|
||||
|
||||
#define FC_NOTIFY_READ_FD(tdata) (tdata)->pipe_fds[0]
|
||||
#define FC_NOTIFY_WRITE_FD(tdata) (tdata)->pipe_fds[1]
|
||||
|
||||
#define ALIGNED_TASK_INFO_SIZE MEM_ALIGN(sizeof(struct fast_task_info))
|
||||
|
||||
|
|
@ -25,92 +37,279 @@ struct nio_thread_data;
|
|||
struct fast_task_info;
|
||||
|
||||
typedef int (*ThreadLoopCallback) (struct nio_thread_data *pThreadData);
|
||||
typedef int (*TaskFinishCallback) (struct fast_task_info *pTask);
|
||||
typedef void (*TaskCleanUpCallback) (struct fast_task_info *pTask);
|
||||
typedef void (*TaskCleanUpCallback) (struct fast_task_info *task);
|
||||
typedef int (*TaskInitCallback)(struct fast_task_info *task, void *arg);
|
||||
typedef void (*TaskReleaseCallback)(struct fast_task_info *task);
|
||||
|
||||
typedef void (*IOEventCallback) (int sock, short event, void *arg);
|
||||
typedef void (*IOEventCallback) (int sock, const int event, void *arg);
|
||||
typedef int (*TaskContinueCallback)(struct fast_task_info *task);
|
||||
|
||||
struct sf_network_handler;
|
||||
struct fast_task_info;
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
#define FC_URING_OP_TYPE(task) (task)->uring.op_type
|
||||
#define FC_URING_IS_CLIENT(task) (task)->uring.is_client
|
||||
#define FC_URING_IS_SEND_ZC(task) ((task)->uring.op_type == IORING_OP_SEND_ZC)
|
||||
#endif
|
||||
|
||||
typedef struct ioevent_entry
|
||||
{
|
||||
int fd;
|
||||
FastTimerEntry timer;
|
||||
IOEventCallback callback;
|
||||
FastTimerEntry timer; //must first
|
||||
int fd;
|
||||
int res; //just for io_uring, since v1.0.81
|
||||
IOEventCallback callback;
|
||||
} IOEventEntry;
|
||||
|
||||
struct nio_thread_data
|
||||
{
|
||||
struct ioevent_puller ev_puller;
|
||||
struct fast_timer timer;
|
||||
int pipe_fds[2];
|
||||
struct fast_task_info *deleted_list;
|
||||
int pipe_fds[2]; //for notify
|
||||
struct fast_task_info *deleted_list; //tasks for cleanup
|
||||
ThreadLoopCallback thread_loop_callback;
|
||||
ThreadLoopCallback busy_polling_callback;
|
||||
void *arg; //extra argument pointer
|
||||
struct {
|
||||
struct fast_task_info *head;
|
||||
struct fast_task_info *tail;
|
||||
pthread_mutex_t lock;
|
||||
} waiting_queue; //task queue
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
volatile int64_t counter;
|
||||
} notify; //for thread notify
|
||||
|
||||
int timeout_ms; //for restore
|
||||
struct fc_list_head polling_queue; //for RDMA busy polling
|
||||
};
|
||||
|
||||
struct ioevent_notify_entry
|
||||
{
|
||||
IOEventEntry event; //must first
|
||||
struct nio_thread_data *thread_data;
|
||||
};
|
||||
|
||||
struct fast_net_buffer
|
||||
{
|
||||
int size; //alloc size
|
||||
int length; //data length
|
||||
int offset; //current offset
|
||||
char *data; //buffer for write or read
|
||||
};
|
||||
|
||||
struct fast_net_buffer_wrapper
|
||||
{
|
||||
struct fast_net_buffer holder;
|
||||
struct fast_net_buffer *ptr;
|
||||
};
|
||||
|
||||
struct fast_task_queue;
|
||||
struct fast_task_info
|
||||
{
|
||||
IOEventEntry event; //must first
|
||||
char client_ip[IP_ADDRESS_SIZE];
|
||||
void *arg; //extra argument pointer
|
||||
char *data; //buffer for write or recv
|
||||
int size; //alloc size
|
||||
int length; //data length
|
||||
int offset; //current offset
|
||||
int64_t req_count; //request count
|
||||
TaskFinishCallback finish_callback;
|
||||
struct nio_thread_data *thread_data;
|
||||
struct fast_task_info *next;
|
||||
IOEventEntry event; //must first
|
||||
union {
|
||||
char server_ip[IP_ADDRESS_SIZE];
|
||||
char client_ip[IP_ADDRESS_SIZE];
|
||||
};
|
||||
void *arg; //extra argument pointer
|
||||
char *recv_body; //for extra (dynamic) recv buffer
|
||||
|
||||
struct {
|
||||
struct iovec *iovs;
|
||||
int count;
|
||||
} iovec_array; //for writev
|
||||
|
||||
struct fast_net_buffer_wrapper send; //send buffer
|
||||
struct fast_net_buffer_wrapper recv; //recv buffer
|
||||
|
||||
uint16_t port; //peer port
|
||||
|
||||
struct {
|
||||
int8_t is_client;
|
||||
uint8_t op_type;
|
||||
} uring; //just for io_uring, since v1.0.81
|
||||
|
||||
struct {
|
||||
uint8_t current;
|
||||
volatile uint8_t notify;
|
||||
} nio_stages; //stages for network IO
|
||||
volatile int8_t canceled; //if task canceled
|
||||
volatile int8_t shrinked; //if task shrinked, since V1.0.81
|
||||
volatile int reffer_count;
|
||||
int pending_send_count;
|
||||
int64_t req_count; //request count
|
||||
struct {
|
||||
int64_t last_req_count;
|
||||
uint32_t last_calc_time;
|
||||
uint16_t continuous_count;
|
||||
bool in_queue;
|
||||
struct fc_list_head dlink; //for polling queue
|
||||
} polling; //for RDMA busy polling
|
||||
TaskContinueCallback continue_callback; //for continue stage
|
||||
struct nio_thread_data *thread_data;
|
||||
struct sf_network_handler *handler; //network handler for libserverframe nio
|
||||
struct fast_task_info *next; //for free queue and deleted list
|
||||
struct fast_task_info *notify_next; //for nio notify queue
|
||||
struct fast_task_queue *free_queue; //task allocator
|
||||
char conn[0]; //for RDMA connection
|
||||
};
|
||||
|
||||
struct fast_task_queue
|
||||
{
|
||||
struct fast_task_info *head;
|
||||
struct fast_task_info *tail;
|
||||
pthread_mutex_t lock;
|
||||
int max_connections;
|
||||
int alloc_connections;
|
||||
int alloc_task_once;
|
||||
int min_buff_size;
|
||||
int max_buff_size;
|
||||
int arg_size;
|
||||
int block_size;
|
||||
bool malloc_whole_block;
|
||||
int min_buff_size;
|
||||
int max_buff_size;
|
||||
int padding_size; //for last field: conn[0]
|
||||
int arg_size; //for arg pointer
|
||||
int block_size;
|
||||
bool malloc_whole_block;
|
||||
bool double_buffers; //if send buffer and recv buffer are independent
|
||||
bool need_shrink;
|
||||
struct fast_mblock_man allocator;
|
||||
TaskInitCallback init_callback;
|
||||
void *init_arg;
|
||||
TaskReleaseCallback release_callback;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int free_queue_init(const int max_connections, const int min_buff_size, \
|
||||
const int max_buff_size, const int arg_size);
|
||||
int free_queue_init_ex(const int max_connections, const int init_connections,
|
||||
int free_queue_init_ex2(struct fast_task_queue *queue, const char *name,
|
||||
const bool double_buffers, const bool need_shrink,
|
||||
const int max_connections, const int alloc_task_once,
|
||||
const int min_buff_size, const int max_buff_size,
|
||||
const int padding_size, const int arg_size,
|
||||
TaskInitCallback init_callback, void *init_arg);
|
||||
|
||||
static inline int free_queue_init_ex(struct fast_task_queue *queue,
|
||||
const char *name, const bool double_buffers,
|
||||
const bool need_shrink, const int max_connections,
|
||||
const int alloc_task_once, const int min_buff_size,
|
||||
const int max_buff_size, const int arg_size);
|
||||
const int max_buff_size, const int arg_size)
|
||||
{
|
||||
const int padding_size = 0;
|
||||
return free_queue_init_ex2(queue, name, double_buffers, need_shrink,
|
||||
max_connections, alloc_task_once, min_buff_size, max_buff_size,
|
||||
padding_size, arg_size, NULL, NULL);
|
||||
}
|
||||
|
||||
void free_queue_destroy();
|
||||
static inline int free_queue_init(struct fast_task_queue *queue,
|
||||
const int max_connections, const int alloc_task_once,
|
||||
const int min_buff_size, const int max_buff_size)
|
||||
{
|
||||
const char *name = "";
|
||||
const bool double_buffers = false;
|
||||
const bool need_shrink = true;
|
||||
const int arg_size = 0;
|
||||
return free_queue_init_ex(queue, name, double_buffers,
|
||||
need_shrink, max_connections, alloc_task_once,
|
||||
min_buff_size, max_buff_size, arg_size);
|
||||
}
|
||||
|
||||
int free_queue_push(struct fast_task_info *pTask);
|
||||
struct fast_task_info *free_queue_pop();
|
||||
int free_queue_count();
|
||||
int free_queue_alloc_connections();
|
||||
int free_queue_set_buffer_size(struct fast_task_info *pTask,
|
||||
const int expect_size);
|
||||
int free_queue_realloc_buffer(struct fast_task_info *pTask,
|
||||
const int expect_size);
|
||||
static inline void free_queue_set_release_callback(
|
||||
struct fast_task_queue *queue,
|
||||
TaskReleaseCallback callback)
|
||||
{
|
||||
queue->release_callback = callback;
|
||||
}
|
||||
|
||||
int task_queue_init(struct fast_task_queue *pQueue);
|
||||
int task_queue_push(struct fast_task_queue *pQueue, \
|
||||
struct fast_task_info *pTask);
|
||||
struct fast_task_info *task_queue_pop(struct fast_task_queue *pQueue);
|
||||
int task_queue_count(struct fast_task_queue *pQueue);
|
||||
int task_queue_set_buffer_size(struct fast_task_queue *pQueue,
|
||||
struct fast_task_info *pTask, const int expect_size);
|
||||
int task_queue_realloc_buffer(struct fast_task_queue *pQueue,
|
||||
struct fast_task_info *pTask, const int expect_size);
|
||||
void free_queue_destroy(struct fast_task_queue *queue);
|
||||
|
||||
int task_queue_get_new_buffer_size(const int min_buff_size,
|
||||
static inline struct fast_task_info *free_queue_pop(
|
||||
struct fast_task_queue *queue)
|
||||
{
|
||||
return (struct fast_task_info *)fast_mblock_alloc_object(&queue->allocator);
|
||||
}
|
||||
|
||||
void free_queue_push(struct fast_task_info *task);
|
||||
|
||||
static inline int free_queue_count(struct fast_task_queue *queue)
|
||||
{
|
||||
return queue->allocator.info.element_total_count -
|
||||
queue->allocator.info.element_used_count;
|
||||
}
|
||||
|
||||
static inline int free_queue_alloc_connections(struct fast_task_queue *queue)
|
||||
{
|
||||
return queue->allocator.info.element_total_count;
|
||||
}
|
||||
|
||||
int free_queue_get_new_buffer_size(const int min_buff_size,
|
||||
const int max_buff_size, const int expect_size, int *new_size);
|
||||
|
||||
int free_queue_set_buffer_size(struct fast_task_info *task,
|
||||
struct fast_net_buffer *buffer, const int expect_size);
|
||||
|
||||
static inline int free_queue_set_max_buffer_size(
|
||||
struct fast_task_info *task,
|
||||
struct fast_net_buffer *buffer)
|
||||
{
|
||||
return free_queue_set_buffer_size(task, buffer,
|
||||
task->free_queue->max_buff_size);
|
||||
}
|
||||
|
||||
int free_queue_realloc_buffer(struct fast_task_info *task,
|
||||
struct fast_net_buffer *buffer, const int expect_size);
|
||||
|
||||
static inline int free_queue_realloc_max_buffer(
|
||||
struct fast_task_info *task,
|
||||
struct fast_net_buffer *buffer)
|
||||
{
|
||||
return free_queue_realloc_buffer(task, buffer,
|
||||
task->free_queue->max_buff_size);
|
||||
}
|
||||
|
||||
/* send and recv buffer operations */
|
||||
static inline int free_queue_set_send_buffer_size(
|
||||
struct fast_task_info *task, const int expect_size)
|
||||
{
|
||||
return free_queue_set_buffer_size(task, task->send.ptr, expect_size);
|
||||
}
|
||||
|
||||
static inline int free_queue_set_recv_buffer_size(
|
||||
struct fast_task_info *task, const int expect_size)
|
||||
{
|
||||
return free_queue_set_buffer_size(task, task->recv.ptr, expect_size);
|
||||
}
|
||||
|
||||
static inline int free_queue_set_send_max_buffer_size(
|
||||
struct fast_task_info *task)
|
||||
{
|
||||
return free_queue_set_max_buffer_size(task, task->send.ptr);
|
||||
}
|
||||
|
||||
static inline int free_queue_set_recv_max_buffer_size(
|
||||
struct fast_task_info *task)
|
||||
{
|
||||
return free_queue_set_max_buffer_size(task, task->recv.ptr);
|
||||
}
|
||||
|
||||
static inline int free_queue_realloc_send_buffer(
|
||||
struct fast_task_info *task, const int expect_size)
|
||||
{
|
||||
return free_queue_realloc_buffer(task, task->send.ptr, expect_size);
|
||||
}
|
||||
|
||||
static inline int free_queue_realloc_recv_buffer(
|
||||
struct fast_task_info *task, const int expect_size)
|
||||
{
|
||||
return free_queue_realloc_buffer(task, task->recv.ptr, expect_size);
|
||||
}
|
||||
|
||||
static inline int free_queue_realloc_send_max_buffer(
|
||||
struct fast_task_info *task)
|
||||
{
|
||||
return free_queue_realloc_max_buffer(task, task->send.ptr);
|
||||
}
|
||||
|
||||
static inline int free_queue_realloc_recv_max_buffer(
|
||||
struct fast_task_info *task)
|
||||
{
|
||||
return free_queue_realloc_max_buffer(task, task->recv.ptr);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
277
src/fast_timer.c
277
src/fast_timer.c
|
|
@ -1,37 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "logger.h"
|
||||
#include "fc_memory.h"
|
||||
#include "pthread_func.h"
|
||||
#include "fast_timer.h"
|
||||
|
||||
int fast_timer_init(FastTimer *timer, const int slot_count,
|
||||
const int64_t current_time)
|
||||
{
|
||||
int bytes;
|
||||
if (slot_count <= 0 || current_time <= 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
int bytes;
|
||||
|
||||
timer->slot_count = slot_count;
|
||||
timer->base_time = current_time; //base time for slot 0
|
||||
timer->current_time = current_time;
|
||||
bytes = sizeof(FastTimerSlot) * slot_count;
|
||||
timer->slots = (FastTimerSlot *)malloc(bytes);
|
||||
if (timer->slots == NULL) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
memset(timer->slots, 0, bytes);
|
||||
return 0;
|
||||
if (slot_count <= 0 || current_time <= 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
timer->slot_count = slot_count;
|
||||
timer->base_time = current_time; //base time for slot 0
|
||||
timer->current_time = current_time;
|
||||
bytes = sizeof(FastTimerSlot) * slot_count;
|
||||
timer->slots = (FastTimerSlot *)fc_malloc(bytes);
|
||||
if (timer->slots == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(timer->slots, 0, bytes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fast_timer_destroy(FastTimer *timer)
|
||||
{
|
||||
if (timer->slots != NULL) {
|
||||
free(timer->slots);
|
||||
timer->slots = NULL;
|
||||
}
|
||||
if (timer->slots != NULL) {
|
||||
free(timer->slots);
|
||||
timer->slots = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#define TIMER_GET_SLOT_INDEX(timer, expires) \
|
||||
|
|
@ -40,138 +59,160 @@ void fast_timer_destroy(FastTimer *timer)
|
|||
#define TIMER_GET_SLOT_POINTER(timer, expires) \
|
||||
(timer->slots + TIMER_GET_SLOT_INDEX(timer, expires))
|
||||
|
||||
int fast_timer_add(FastTimer *timer, FastTimerEntry *entry)
|
||||
static inline void add_entry(FastTimer *timer, FastTimerSlot *slot,
|
||||
FastTimerEntry *entry, const int64_t expires, const bool set_expires)
|
||||
{
|
||||
FastTimerSlot *slot;
|
||||
if (set_expires) {
|
||||
entry->expires = expires;
|
||||
}
|
||||
entry->next = slot->head.next;
|
||||
if (slot->head.next != NULL) {
|
||||
slot->head.next->prev = entry;
|
||||
}
|
||||
entry->prev = &slot->head;
|
||||
slot->head.next = entry;
|
||||
entry->rehash = false;
|
||||
}
|
||||
|
||||
slot = TIMER_GET_SLOT_POINTER(timer, entry->expires >
|
||||
timer->current_time ? entry->expires : timer->current_time);
|
||||
entry->next = slot->head.next;
|
||||
if (slot->head.next != NULL) {
|
||||
slot->head.next->prev = entry;
|
||||
}
|
||||
entry->prev = &slot->head;
|
||||
slot->head.next = entry;
|
||||
entry->rehash = false;
|
||||
return 0;
|
||||
void fast_timer_add_ex(FastTimer *timer, FastTimerEntry *entry,
|
||||
const int64_t expires, const bool set_expires)
|
||||
{
|
||||
FastTimerSlot *slot;
|
||||
int64_t new_expires;
|
||||
bool new_set_expires;
|
||||
|
||||
if (expires > timer->current_time) {
|
||||
new_expires = expires;
|
||||
new_set_expires = set_expires;
|
||||
} else {
|
||||
new_expires = timer->current_time + 1; //plus 1 for rare case
|
||||
new_set_expires = true;
|
||||
}
|
||||
slot = TIMER_GET_SLOT_POINTER(timer, new_expires);
|
||||
add_entry(timer, slot, entry, new_expires, new_set_expires);
|
||||
}
|
||||
|
||||
int fast_timer_modify(FastTimer *timer, FastTimerEntry *entry,
|
||||
const int64_t new_expires)
|
||||
{
|
||||
if (new_expires == entry->expires) {
|
||||
int result;
|
||||
|
||||
if (new_expires > entry->expires) {
|
||||
entry->rehash = TIMER_GET_SLOT_INDEX(timer, new_expires) !=
|
||||
TIMER_GET_SLOT_INDEX(timer, entry->expires);
|
||||
entry->expires = new_expires; //lazy move
|
||||
} else if (new_expires < entry->expires) {
|
||||
if ((result=fast_timer_remove(timer, entry)) == 0) {
|
||||
fast_timer_add_ex(timer, entry, new_expires, true);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (new_expires < entry->expires) {
|
||||
fast_timer_remove(timer, entry);
|
||||
entry->expires = new_expires;
|
||||
return fast_timer_add(timer, entry);
|
||||
}
|
||||
|
||||
entry->rehash = TIMER_GET_SLOT_INDEX(timer, new_expires) !=
|
||||
TIMER_GET_SLOT_INDEX(timer, entry->expires);
|
||||
entry->expires = new_expires; //lazy move
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fast_timer_remove(FastTimer *timer, FastTimerEntry *entry)
|
||||
{
|
||||
if (entry->prev == NULL) {
|
||||
return ENOENT; //already removed
|
||||
}
|
||||
if (entry->prev == NULL) {
|
||||
return ENOENT; //already removed
|
||||
}
|
||||
|
||||
if (entry->next != NULL) {
|
||||
entry->next->prev = entry->prev;
|
||||
entry->prev->next = entry->next;
|
||||
entry->next = NULL;
|
||||
}
|
||||
else {
|
||||
entry->prev->next = NULL;
|
||||
}
|
||||
if (entry->next != NULL) {
|
||||
entry->next->prev = entry->prev;
|
||||
entry->prev->next = entry->next;
|
||||
entry->next = NULL;
|
||||
} else {
|
||||
entry->prev->next = NULL;
|
||||
}
|
||||
|
||||
entry->prev = NULL;
|
||||
return 0;
|
||||
entry->prev = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FastTimerSlot *fast_timer_slot_get(FastTimer *timer, const int64_t current_time)
|
||||
{
|
||||
if (timer->current_time >= current_time) {
|
||||
return NULL;
|
||||
}
|
||||
if (timer->current_time >= current_time) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||
return TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||
}
|
||||
|
||||
int fast_timer_timeouts_get(FastTimer *timer, const int64_t current_time,
|
||||
FastTimerEntry *head)
|
||||
FastTimerEntry *head)
|
||||
{
|
||||
FastTimerSlot *slot;
|
||||
FastTimerEntry *entry;
|
||||
FastTimerEntry *first;
|
||||
FastTimerEntry *last;
|
||||
FastTimerEntry *tail;
|
||||
int count;
|
||||
FastTimerSlot *slot;
|
||||
FastTimerSlot *new_slot;
|
||||
FastTimerEntry *entry;
|
||||
FastTimerEntry *first;
|
||||
FastTimerEntry *last;
|
||||
FastTimerEntry *tail;
|
||||
int count;
|
||||
|
||||
head->prev = NULL;
|
||||
head->next = NULL;
|
||||
if (timer->current_time >= current_time) {
|
||||
return 0;
|
||||
}
|
||||
head->prev = NULL;
|
||||
head->next = NULL;
|
||||
if (timer->current_time >= current_time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
first = NULL;
|
||||
last = NULL;
|
||||
tail = head;
|
||||
count = 0;
|
||||
while (timer->current_time < current_time) {
|
||||
slot = TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||
entry = slot->head.next;
|
||||
while (entry != NULL) {
|
||||
if (entry->expires >= current_time) { //not expired
|
||||
if (first != NULL) {
|
||||
first->prev->next = entry;
|
||||
entry->prev = first->prev;
|
||||
first = NULL;
|
||||
last = NULL;
|
||||
tail = head;
|
||||
count = 0;
|
||||
while (timer->current_time < current_time) {
|
||||
slot = TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||
entry = slot->head.next;
|
||||
while (entry != NULL) {
|
||||
if (entry->expires >= current_time) { //not expired
|
||||
if (first != NULL) {
|
||||
first->prev->next = entry;
|
||||
entry->prev = first->prev;
|
||||
|
||||
tail->next = first;
|
||||
first->prev = tail;
|
||||
tail = last;
|
||||
first = NULL;
|
||||
}
|
||||
if (entry->rehash) {
|
||||
last = entry;
|
||||
entry = entry->next;
|
||||
|
||||
new_slot = TIMER_GET_SLOT_POINTER(timer, last->expires);
|
||||
if (new_slot != slot) { //check to avoid deadlock
|
||||
if (fast_timer_remove(timer, last) == 0) {
|
||||
add_entry(timer, new_slot, last,
|
||||
last->expires, false);
|
||||
}
|
||||
} else {
|
||||
last->rehash = false;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
} else { //expired
|
||||
count++;
|
||||
if (first == NULL) {
|
||||
first = entry;
|
||||
}
|
||||
}
|
||||
|
||||
last = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
if (first != NULL) {
|
||||
first->prev->next = NULL;
|
||||
|
||||
tail->next = first;
|
||||
first->prev = tail;
|
||||
tail = last;
|
||||
first = NULL;
|
||||
}
|
||||
if (entry->rehash) {
|
||||
last = entry;
|
||||
entry = entry->next;
|
||||
|
||||
last->rehash = false;
|
||||
fast_timer_remove(timer, last);
|
||||
fast_timer_add(timer, last);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else { //expired
|
||||
count++;
|
||||
if (first == NULL) {
|
||||
first = entry;
|
||||
}
|
||||
}
|
||||
|
||||
last = entry;
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
if (first != NULL) {
|
||||
first->prev->next = NULL;
|
||||
|
||||
tail->next = first;
|
||||
first->prev = tail;
|
||||
tail = last;
|
||||
first = NULL;
|
||||
if (count > 0) {
|
||||
tail->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
tail->next = NULL;
|
||||
}
|
||||
|
||||
return count;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __FAST_TIMER_H__
|
||||
#define __FAST_TIMER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include "common_define.h"
|
||||
|
||||
struct fast_timer_slot;
|
||||
typedef struct fast_timer_entry {
|
||||
int64_t expires;
|
||||
void *data;
|
||||
struct fast_timer_entry *prev;
|
||||
struct fast_timer_entry *next;
|
||||
bool rehash;
|
||||
int64_t expires;
|
||||
struct fast_timer_entry *prev;
|
||||
struct fast_timer_entry *next;
|
||||
int slot_index;
|
||||
bool rehash;
|
||||
} FastTimerEntry;
|
||||
|
||||
typedef struct fast_timer_slot {
|
||||
struct fast_timer_entry head;
|
||||
struct fast_timer_entry head;
|
||||
} FastTimerSlot;
|
||||
|
||||
typedef struct fast_timer {
|
||||
int slot_count; //time wheel slot count
|
||||
int64_t base_time; //base time for slot 0
|
||||
int64_t current_time;
|
||||
FastTimerSlot *slots;
|
||||
int slot_count; //time wheel slot count
|
||||
int64_t base_time; //base time for slot 0
|
||||
volatile int64_t current_time;
|
||||
FastTimerSlot *slots;
|
||||
} FastTimer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define fast_timer_add(timer, entry) \
|
||||
fast_timer_add_ex(timer, entry, (entry)->expires, false)
|
||||
|
||||
int fast_timer_init(FastTimer *timer, const int slot_count,
|
||||
const int64_t current_time);
|
||||
void fast_timer_destroy(FastTimer *timer);
|
||||
|
||||
int fast_timer_add(FastTimer *timer, FastTimerEntry *entry);
|
||||
void fast_timer_add_ex(FastTimer *timer, FastTimerEntry *entry,
|
||||
const int64_t expires, const bool set_expires);
|
||||
int fast_timer_remove(FastTimer *timer, FastTimerEntry *entry);
|
||||
int fast_timer_modify(FastTimer *timer, FastTimerEntry *entry,
|
||||
const int64_t new_expires);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _FC_ATOMIC_H
|
||||
#define _FC_ATOMIC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FC_ATOMIC_GET(var) __sync_add_and_fetch(&var, 0)
|
||||
|
||||
#define FC_ATOMIC_INC(var) __sync_add_and_fetch(&var, 1)
|
||||
#define FC_ATOMIC_DEC(var) __sync_sub_and_fetch(&var, 1)
|
||||
|
||||
#define FC_ATOMIC_INC_EX(var, n) __sync_add_and_fetch(&var, n)
|
||||
#define FC_ATOMIC_DEC_EX(var, n) __sync_sub_and_fetch(&var, n)
|
||||
|
||||
#define FC_ATOMIC_CAS(var, old_value, new_value) \
|
||||
do { \
|
||||
if (__sync_bool_compare_and_swap(&var, old_value, new_value)) { \
|
||||
break; \
|
||||
} \
|
||||
old_value = __sync_add_and_fetch(&var, 0); \
|
||||
} while (new_value != old_value)
|
||||
|
||||
|
||||
#define FC_ATOMIC_SET(var, new_value) \
|
||||
do { \
|
||||
typeof(var) _old_value; \
|
||||
_old_value = var; \
|
||||
do { \
|
||||
if (__sync_bool_compare_and_swap(&var, _old_value, new_value)) { \
|
||||
break; \
|
||||
} \
|
||||
_old_value = __sync_add_and_fetch(&var, 0); \
|
||||
} while (new_value != _old_value); \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define FC_ATOMIC_SET_BY_CONDITION(var, value, skip_operator) \
|
||||
do { \
|
||||
typeof(var) old; \
|
||||
old = __sync_add_and_fetch(&var, 0); \
|
||||
if (value skip_operator old) { \
|
||||
break; \
|
||||
} \
|
||||
if (__sync_bool_compare_and_swap(&var, old, value)) { \
|
||||
break; \
|
||||
} \
|
||||
} while (1)
|
||||
|
||||
|
||||
#define FC_ATOMIC_SET_LARGER(var, value) \
|
||||
FC_ATOMIC_SET_BY_CONDITION(var, value, <=)
|
||||
|
||||
#define FC_ATOMIC_SET_SMALLER(var, value) \
|
||||
FC_ATOMIC_SET_BY_CONDITION(var, value, >=)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -25,7 +25,6 @@ fc_list_add (struct fc_list_head *_new, struct fc_list_head *head)
|
|||
_new->next->prev = _new;
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
fc_list_add_tail (struct fc_list_head *_new, struct fc_list_head *head)
|
||||
{
|
||||
|
|
@ -37,7 +36,27 @@ fc_list_add_tail (struct fc_list_head *_new, struct fc_list_head *head)
|
|||
}
|
||||
|
||||
static inline void
|
||||
fc_list_add_internal(struct fc_list_head *_new, struct fc_list_head *prev,
|
||||
fc_list_add_before (struct fc_list_head *_new, struct fc_list_head *current)
|
||||
{
|
||||
_new->prev = current->prev;
|
||||
_new->next = current;
|
||||
|
||||
_new->prev->next = _new;
|
||||
_new->next->prev = _new;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fc_list_add_after (struct fc_list_head *_new, struct fc_list_head *current)
|
||||
{
|
||||
_new->prev = current;
|
||||
_new->next = current->next;
|
||||
|
||||
current->next->prev = _new;
|
||||
current->next = _new;
|
||||
}
|
||||
|
||||
static inline void
|
||||
fc_list_add_internal (struct fc_list_head *_new, struct fc_list_head *prev,
|
||||
struct fc_list_head *next)
|
||||
{
|
||||
next->prev = _new;
|
||||
|
|
@ -147,6 +166,15 @@ static inline int fc_list_count(struct fc_list_head *head)
|
|||
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
|
||||
|
||||
|
||||
#define fc_list_first_entry(head, type, member) \
|
||||
((head)->next == head ? NULL : \
|
||||
fc_list_entry((head)->next, type, member))
|
||||
|
||||
#define fc_list_last_entry(head, type, member) \
|
||||
((head)->prev == head ? NULL : \
|
||||
fc_list_entry((head)->prev, type, member))
|
||||
|
||||
|
||||
#define fc_list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fc_memory.c
|
||||
|
||||
#include "fc_memory.h"
|
||||
|
||||
fc_memory_oom_notify_func g_oom_notify = NULL;
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fc_memory.h
|
||||
|
||||
#ifndef _FC_MEMORY_H
|
||||
#define _FC_MEMORY_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "common_define.h"
|
||||
#include "logger.h"
|
||||
|
||||
typedef void (*fc_memory_oom_notify_func)(const size_t curr_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern fc_memory_oom_notify_func g_oom_notify;
|
||||
|
||||
static inline void *fc_malloc_ex(const char *file,
|
||||
const int line, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = malloc(size);
|
||||
if (ptr == NULL) {
|
||||
logError("file: %s, line: %d, malloc %"PRId64" bytes fail, "
|
||||
"errno: %d, error info: %s", file, line,
|
||||
(int64_t)size, errno, STRERROR(errno));
|
||||
if (g_oom_notify != NULL) {
|
||||
g_oom_notify(size);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static inline void *fc_realloc_ex(const char *file,
|
||||
const int line, void *ptr, size_t size)
|
||||
{
|
||||
void *new_ptr;
|
||||
new_ptr = realloc(ptr, size);
|
||||
if (new_ptr == NULL) {
|
||||
logError("file: %s, line: %d, realloc %"PRId64" bytes fail, "
|
||||
"errno: %d, error info: %s", file, line,
|
||||
(int64_t)size, errno, STRERROR(errno));
|
||||
if (g_oom_notify != NULL) {
|
||||
g_oom_notify(size);
|
||||
}
|
||||
}
|
||||
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
static inline char *fc_strdup_ex(const char *file,
|
||||
const int line, const char *str)
|
||||
{
|
||||
char *output;
|
||||
int len;
|
||||
|
||||
len = strlen(str);
|
||||
output = (char *)fc_malloc_ex(file, line, len + 1);
|
||||
if (output == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
memcpy(output, str, len);
|
||||
}
|
||||
*(output + len) = '\0';
|
||||
return output;
|
||||
}
|
||||
|
||||
static inline void *fc_calloc_ex(const char *file,
|
||||
const int line, size_t count, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = calloc(count, size);
|
||||
if (ptr == NULL) {
|
||||
logError("file: %s, line: %d, malloc %"PRId64" bytes fail, "
|
||||
"errno: %d, error info: %s", file, line,
|
||||
(int64_t)(count * size), errno, STRERROR(errno));
|
||||
if (g_oom_notify != NULL) {
|
||||
g_oom_notify(count * size);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#define fc_malloc(size) fc_malloc_ex(__FILE__, __LINE__, size)
|
||||
#define fc_realloc(ptr, size) fc_realloc_ex(__FILE__, __LINE__, ptr, size)
|
||||
#define fc_calloc(count, size) fc_calloc_ex(__FILE__, __LINE__, count, size)
|
||||
#define fc_strdup(str) fc_strdup_ex(__FILE__, __LINE__, str)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fc_queue.c
|
||||
|
||||
#include "pthread_func.h"
|
||||
#include "fc_queue.h"
|
||||
|
||||
int fc_queue_init(struct fc_queue *queue, const int next_ptr_offset)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=init_pthread_lock_cond_pair(&queue->lcp)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
queue->head = NULL;
|
||||
queue->tail = NULL;
|
||||
queue->next_ptr_offset = next_ptr_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fc_queue_destroy(struct fc_queue *queue)
|
||||
{
|
||||
destroy_pthread_lock_cond_pair(&queue->lcp);
|
||||
}
|
||||
|
||||
void fc_queue_push_ex(struct fc_queue *queue, void *data, bool *notify)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
FC_QUEUE_NEXT_PTR(queue, data) = NULL;
|
||||
if (queue->tail == NULL) {
|
||||
queue->head = data;
|
||||
*notify = true;
|
||||
} else {
|
||||
FC_QUEUE_NEXT_PTR(queue, queue->tail) = data;
|
||||
*notify = false;
|
||||
}
|
||||
queue->tail = data;
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
}
|
||||
|
||||
static inline bool fc_queue_exists(struct fc_queue *queue, void *data)
|
||||
{
|
||||
void *current;
|
||||
if (queue->head == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current = queue->head;
|
||||
do {
|
||||
if (current == data) {
|
||||
return true;
|
||||
}
|
||||
current = FC_QUEUE_NEXT_PTR(queue, current);
|
||||
} while (current != NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int fc_queue_push_with_check_ex(struct fc_queue *queue,
|
||||
void *data, bool *notify)
|
||||
{
|
||||
int result;
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
if (fc_queue_exists(queue, data)) {
|
||||
result = EEXIST;
|
||||
*notify = false;
|
||||
} else {
|
||||
result = 0;
|
||||
FC_QUEUE_NEXT_PTR(queue, data) = NULL;
|
||||
if (queue->tail == NULL) {
|
||||
queue->head = data;
|
||||
*notify = true;
|
||||
} else {
|
||||
FC_QUEUE_NEXT_PTR(queue, queue->tail) = data;
|
||||
*notify = false;
|
||||
}
|
||||
queue->tail = data;
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void *fc_queue_pop_ex(struct fc_queue *queue, const bool blocked)
|
||||
{
|
||||
void *data;
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
do {
|
||||
data = queue->head;
|
||||
if (data == NULL) {
|
||||
if (!blocked) {
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_cond_wait(&queue->lcp.cond, &queue->lcp.lock);
|
||||
data = queue->head;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
queue->head = FC_QUEUE_NEXT_PTR(queue, data);
|
||||
if (queue->head == NULL) {
|
||||
queue->tail = NULL;
|
||||
}
|
||||
}
|
||||
} while (0);
|
||||
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
return data;
|
||||
}
|
||||
|
||||
void *fc_queue_pop_all_ex(struct fc_queue *queue, const bool blocked)
|
||||
{
|
||||
void *data;
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
do {
|
||||
data = queue->head;
|
||||
if (data == NULL) {
|
||||
if (!blocked) {
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_cond_wait(&queue->lcp.cond, &queue->lcp.lock);
|
||||
data = queue->head;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
queue->head = queue->tail = NULL;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
return data;
|
||||
}
|
||||
|
||||
void fc_queue_push_queue_to_head_ex(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo, bool *notify)
|
||||
{
|
||||
if (qinfo->head == NULL) {
|
||||
*notify = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
FC_QUEUE_NEXT_PTR(queue, qinfo->tail) = queue->head;
|
||||
queue->head = qinfo->head;
|
||||
if (queue->tail == NULL) {
|
||||
queue->tail = qinfo->tail;
|
||||
*notify = true;
|
||||
} else {
|
||||
*notify = false;
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
}
|
||||
|
||||
void fc_queue_push_queue_to_tail_ex(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo, bool *notify)
|
||||
{
|
||||
if (qinfo->head == NULL) {
|
||||
*notify = false;
|
||||
return;
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
if (queue->head == NULL) {
|
||||
queue->head = qinfo->head;
|
||||
*notify = true;
|
||||
} else {
|
||||
FC_QUEUE_NEXT_PTR(queue, queue->tail) = qinfo->head;
|
||||
*notify = false;
|
||||
}
|
||||
queue->tail = qinfo->tail;
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
}
|
||||
|
||||
void fc_queue_pop_to_queue_ex(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo, const bool blocked)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
if (queue->head == NULL) {
|
||||
if (blocked) {
|
||||
pthread_cond_wait(&queue->lcp.cond, &queue->lcp.lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (queue->head != NULL) {
|
||||
qinfo->head = queue->head;
|
||||
qinfo->tail = queue->tail;
|
||||
queue->head = queue->tail = NULL;
|
||||
} else {
|
||||
qinfo->head = qinfo->tail = NULL;
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
}
|
||||
|
||||
void *fc_queue_timedpop(struct fc_queue *queue,
|
||||
const int timeout, const int time_unit)
|
||||
{
|
||||
void *data;
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
data = queue->head;
|
||||
if (data == NULL) {
|
||||
fc_cond_timedwait(&queue->lcp, timeout, time_unit);
|
||||
data = queue->head;
|
||||
}
|
||||
|
||||
if (data != NULL) {
|
||||
queue->head = FC_QUEUE_NEXT_PTR(queue, data);
|
||||
if (queue->head == NULL) {
|
||||
queue->tail = NULL;
|
||||
}
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void *fc_queue_timedpeek(struct fc_queue *queue,
|
||||
const int timeout, const int time_unit)
|
||||
{
|
||||
void *data;
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&queue->lcp.lock);
|
||||
data = queue->head;
|
||||
if (data == NULL) {
|
||||
fc_cond_timedwait(&queue->lcp, timeout, time_unit);
|
||||
data = queue->head;
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&queue->lcp.lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
int fc_queue_alloc_chain(struct fc_queue *queue, struct fast_mblock_man
|
||||
*mblock, const int count, struct fc_queue_info *chain)
|
||||
{
|
||||
struct fast_mblock_node *node;
|
||||
|
||||
if ((node=fast_mblock_batch_alloc1(mblock, count)) == NULL) {
|
||||
chain->head = chain->tail = NULL;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
chain->head = chain->tail = node->data;
|
||||
while ((node=node->next) != NULL) {
|
||||
FC_QUEUE_NEXT_PTR(queue, chain->tail) = node->data;
|
||||
chain->tail = node->data;
|
||||
}
|
||||
FC_QUEUE_NEXT_PTR(queue, chain->tail) = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fc_queue_free_chain(struct fc_queue *queue, struct fast_mblock_man
|
||||
*mblock, struct fc_queue_info *qinfo)
|
||||
{
|
||||
struct fast_mblock_node *previous;
|
||||
struct fast_mblock_node *current;
|
||||
struct fast_mblock_chain chain;
|
||||
void *data;
|
||||
|
||||
if (qinfo->head == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
chain.head = previous = fast_mblock_to_node_ptr(qinfo->head);
|
||||
data = FC_QUEUE_NEXT_PTR(queue, qinfo->head);
|
||||
while (data != NULL) {
|
||||
current = fast_mblock_to_node_ptr(data);
|
||||
previous->next = current;
|
||||
|
||||
previous = current;
|
||||
data = FC_QUEUE_NEXT_PTR(queue, data);
|
||||
}
|
||||
|
||||
previous->next = NULL;
|
||||
chain.tail = previous;
|
||||
return fast_mblock_batch_free(mblock, &chain);
|
||||
}
|
||||
|
||||
int fc_queue_remove(struct fc_queue *queue, void *data)
|
||||
{
|
||||
void *previous;
|
||||
void *current;
|
||||
int result;
|
||||
|
||||
pthread_mutex_lock(&queue->lcp.lock);
|
||||
if (queue->head == NULL)
|
||||
{
|
||||
result = ENOENT;
|
||||
}
|
||||
else if (queue->head == data)
|
||||
{
|
||||
queue->head = FC_QUEUE_NEXT_PTR(queue, queue->head);
|
||||
if (queue->head == NULL)
|
||||
{
|
||||
queue->tail = NULL;
|
||||
}
|
||||
result = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = ENOENT;
|
||||
previous = queue->head;
|
||||
while ((current=FC_QUEUE_NEXT_PTR(queue, previous)) != NULL)
|
||||
{
|
||||
if (current == data)
|
||||
{
|
||||
FC_QUEUE_NEXT_PTR(queue, previous) =
|
||||
FC_QUEUE_NEXT_PTR(queue, current);
|
||||
if (queue->tail == current)
|
||||
{
|
||||
queue->tail = previous;
|
||||
}
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&queue->lcp.lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//fc_queue.h
|
||||
|
||||
#ifndef _FC_QUEUE_H
|
||||
#define _FC_QUEUE_H
|
||||
|
||||
#include "common_define.h"
|
||||
#include "fast_mblock.h"
|
||||
|
||||
struct fc_queue_info
|
||||
{
|
||||
void *head;
|
||||
void *tail;
|
||||
};
|
||||
|
||||
struct fc_queue
|
||||
{
|
||||
void *head;
|
||||
void *tail;
|
||||
pthread_lock_cond_pair_t lcp;
|
||||
int next_ptr_offset;
|
||||
};
|
||||
|
||||
|
||||
#define FC_QUEUE_NEXT_PTR(queue, data) \
|
||||
*((void **)(((char *)data) + (queue)->next_ptr_offset))
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int fc_queue_init(struct fc_queue *queue, const int next_ptr_offset);
|
||||
|
||||
void fc_queue_destroy(struct fc_queue *queue);
|
||||
|
||||
static inline void fc_queue_terminate(struct fc_queue *queue)
|
||||
{
|
||||
pthread_cond_signal(&queue->lcp.cond);
|
||||
}
|
||||
|
||||
static inline void fc_queue_terminate_all(
|
||||
struct fc_queue *queue, const int count)
|
||||
{
|
||||
int i;
|
||||
for (i=0; i<count; i++) {
|
||||
pthread_cond_signal(&(queue->lcp.cond));
|
||||
}
|
||||
}
|
||||
|
||||
#define fc_queue_notify(queue) fc_queue_terminate(queue)
|
||||
|
||||
#define fc_queue_notify_all(queue, count) \
|
||||
fc_queue_terminate_all(queue, count)
|
||||
|
||||
//notify by the caller
|
||||
void fc_queue_push_ex(struct fc_queue *queue, void *data, bool *notify);
|
||||
int fc_queue_push_with_check_ex(struct fc_queue *queue,
|
||||
void *data, bool *notify);
|
||||
|
||||
static inline void fc_queue_push(struct fc_queue *queue, void *data)
|
||||
{
|
||||
bool notify;
|
||||
|
||||
fc_queue_push_ex(queue, data, ¬ify);
|
||||
if (notify) {
|
||||
pthread_cond_signal(&(queue->lcp.cond));
|
||||
}
|
||||
}
|
||||
|
||||
static inline int fc_queue_push_with_check(struct fc_queue *queue, void *data)
|
||||
{
|
||||
int result;
|
||||
bool notify;
|
||||
|
||||
result = fc_queue_push_with_check_ex(queue, data, ¬ify);
|
||||
if (notify) {
|
||||
pthread_cond_signal(&(queue->lcp.cond));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline void fc_queue_push_silence(struct fc_queue *queue, void *data)
|
||||
{
|
||||
bool notify;
|
||||
fc_queue_push_ex(queue, data, ¬ify);
|
||||
}
|
||||
|
||||
void fc_queue_push_queue_to_head_ex(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo, bool *notify);
|
||||
|
||||
static inline void fc_queue_push_queue_to_head(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo)
|
||||
{
|
||||
bool notify;
|
||||
|
||||
fc_queue_push_queue_to_head_ex(queue, qinfo, ¬ify);
|
||||
if (notify) {
|
||||
pthread_cond_signal(&(queue->lcp.cond));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fc_queue_push_queue_to_head_silence(
|
||||
struct fc_queue *queue, struct fc_queue_info *qinfo)
|
||||
{
|
||||
bool notify;
|
||||
fc_queue_push_queue_to_head_ex(queue, qinfo, ¬ify);
|
||||
}
|
||||
|
||||
void fc_queue_push_queue_to_tail_ex(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo, bool *notify);
|
||||
|
||||
static inline void fc_queue_push_queue_to_tail(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo)
|
||||
{
|
||||
bool notify;
|
||||
|
||||
fc_queue_push_queue_to_tail_ex(queue, qinfo, ¬ify);
|
||||
if (notify) {
|
||||
pthread_cond_signal(&(queue->lcp.cond));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fc_queue_push_queue_to_tail_silence(
|
||||
struct fc_queue *queue, struct fc_queue_info *qinfo)
|
||||
{
|
||||
bool notify;
|
||||
fc_queue_push_queue_to_tail_ex(queue, qinfo, ¬ify);
|
||||
}
|
||||
|
||||
void *fc_queue_pop_ex(struct fc_queue *queue, const bool blocked);
|
||||
#define fc_queue_pop(queue) fc_queue_pop_ex(queue, true)
|
||||
#define fc_queue_try_pop(queue) fc_queue_pop_ex(queue, false)
|
||||
|
||||
void *fc_queue_pop_all_ex(struct fc_queue *queue, const bool blocked);
|
||||
#define fc_queue_pop_all(queue) fc_queue_pop_all_ex(queue, true)
|
||||
#define fc_queue_try_pop_all(queue) fc_queue_pop_all_ex(queue, false)
|
||||
|
||||
void fc_queue_pop_to_queue_ex(struct fc_queue *queue,
|
||||
struct fc_queue_info *qinfo, const bool blocked);
|
||||
|
||||
#define fc_queue_pop_to_queue(queue, qinfo) \
|
||||
fc_queue_pop_to_queue_ex(queue, qinfo, true)
|
||||
|
||||
#define fc_queue_try_pop_to_queue(queue, qinfo) \
|
||||
fc_queue_pop_to_queue_ex(queue, qinfo, false)
|
||||
|
||||
static inline bool fc_queue_empty(struct fc_queue *queue)
|
||||
{
|
||||
bool empty;
|
||||
|
||||
pthread_mutex_lock(&queue->lcp.lock);
|
||||
empty = (queue->head == NULL);
|
||||
pthread_mutex_unlock(&queue->lcp.lock);
|
||||
return empty;
|
||||
}
|
||||
|
||||
static inline int fc_queue_count(struct fc_queue *queue)
|
||||
{
|
||||
int count;
|
||||
void *data;
|
||||
|
||||
count = 0;
|
||||
pthread_mutex_lock(&queue->lcp.lock);
|
||||
data = queue->head;
|
||||
while (data != NULL)
|
||||
{
|
||||
++count;
|
||||
data = FC_QUEUE_NEXT_PTR(queue, data);
|
||||
}
|
||||
pthread_mutex_unlock(&queue->lcp.lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline void *fc_queue_peek(struct fc_queue *queue)
|
||||
{
|
||||
void *data;
|
||||
|
||||
pthread_mutex_lock(&queue->lcp.lock);
|
||||
data = queue->head;
|
||||
pthread_mutex_unlock(&queue->lcp.lock);
|
||||
return data;
|
||||
}
|
||||
|
||||
void *fc_queue_timedpop(struct fc_queue *queue,
|
||||
const int timeout, const int time_unit);
|
||||
|
||||
#define fc_queue_timedpop_sec(queue, timeout) \
|
||||
fc_queue_timedpop(queue, timeout, FC_TIME_UNIT_SECOND)
|
||||
|
||||
#define fc_queue_timedpop_ms(queue, timeout_ms) \
|
||||
fc_queue_timedpop(queue, timeout_ms, FC_TIME_UNIT_MSECOND)
|
||||
|
||||
#define fc_queue_timedpop_us(queue, timeout_us) \
|
||||
fc_queue_timedpop(queue, timeout_us, FC_TIME_UNIT_USECOND)
|
||||
|
||||
void *fc_queue_timedpeek(struct fc_queue *queue,
|
||||
const int timeout, const int time_unit);
|
||||
|
||||
#define fc_queue_timedpeek_sec(queue, timeout) \
|
||||
fc_queue_timedpeek(queue, timeout, FC_TIME_UNIT_SECOND)
|
||||
|
||||
#define fc_queue_timedpeek_ms(queue, timeout_ms) \
|
||||
fc_queue_timedpeek(queue, timeout_ms, FC_TIME_UNIT_MSECOND)
|
||||
|
||||
#define fc_queue_timedpeek_us(queue, timeout_us) \
|
||||
fc_queue_timedpeek(queue, timeout_us, FC_TIME_UNIT_USECOND)
|
||||
|
||||
int fc_queue_alloc_chain(struct fc_queue *queue, struct fast_mblock_man
|
||||
*mblock, const int count, struct fc_queue_info *chain);
|
||||
|
||||
int fc_queue_free_chain(struct fc_queue *queue, struct fast_mblock_man
|
||||
*mblock, struct fc_queue_info *qinfo);
|
||||
|
||||
int fc_queue_remove(struct fc_queue *queue, void *data);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2015 Happy Fish / YuQing
|
||||
*
|
||||
* libfastcommon may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//flat_skiplist.c
|
||||
|
||||
|
|
@ -15,12 +22,15 @@
|
|||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include "logger.h"
|
||||
#include "fc_memory.h"
|
||||
#include "flat_skiplist.h"
|
||||
|
||||
int flat_skiplist_init_ex(FlatSkiplist *sl, const int level_count,
|
||||
skiplist_compare_func compare_func, skiplist_free_func free_func,
|
||||
const int min_alloc_elements_once)
|
||||
{
|
||||
const int64_t alloc_elements_limit = 0;
|
||||
char name[64];
|
||||
int bytes;
|
||||
int element_size;
|
||||
int i;
|
||||
|
|
@ -43,21 +53,15 @@ int flat_skiplist_init_ex(FlatSkiplist *sl, const int level_count,
|
|||
}
|
||||
|
||||
bytes = sizeof(FlatSkiplistNode *) * level_count;
|
||||
sl->tmp_previous = (FlatSkiplistNode **)malloc(bytes);
|
||||
sl->tmp_previous = (FlatSkiplistNode **)fc_malloc(bytes);
|
||||
if (sl->tmp_previous == NULL) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
bytes = sizeof(struct fast_mblock_man) * level_count;
|
||||
sl->mblocks = (struct fast_mblock_man *)malloc(bytes);
|
||||
sl->mblocks = (struct fast_mblock_man *)fc_malloc(bytes);
|
||||
if (sl->mblocks == NULL) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(sl->mblocks, 0, bytes);
|
||||
|
||||
|
|
@ -70,9 +74,12 @@ int flat_skiplist_init_ex(FlatSkiplist *sl, const int level_count,
|
|||
}
|
||||
|
||||
for (i=level_count-1; i>=0; i--) {
|
||||
element_size = sizeof(FlatSkiplistNode) + sizeof(FlatSkiplistNode *) * (i + 1);
|
||||
if ((result=fast_mblock_init_ex(sl->mblocks + i,
|
||||
element_size, alloc_elements_once, NULL, false)) != 0)
|
||||
sprintf(name, "flat-sl-level%02d", i);
|
||||
element_size = sizeof(FlatSkiplistNode) +
|
||||
sizeof(FlatSkiplistNode *) * (i + 1);
|
||||
if ((result=fast_mblock_init_ex1(sl->mblocks + i, name,
|
||||
element_size, alloc_elements_once, alloc_elements_limit,
|
||||
NULL, NULL, false)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
@ -363,6 +370,16 @@ int flat_skiplist_find_all(FlatSkiplist *sl, void *data, FlatSkiplistIterator *i
|
|||
return 0;
|
||||
}
|
||||
|
||||
void *flat_skiplist_find_ge(FlatSkiplist *sl, void *data)
|
||||
{
|
||||
FlatSkiplistNode *node;
|
||||
node = flat_skiplist_get_first_larger_or_equal(sl, data);
|
||||
if (node == sl->top) {
|
||||
return NULL;
|
||||
}
|
||||
return node->data;
|
||||
}
|
||||
|
||||
int flat_skiplist_find_range(FlatSkiplist *sl, void *start_data, void *end_data,
|
||||
FlatSkiplistIterator *iterator)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2015 Happy Fish / YuQing
|
||||
*
|
||||
* libfastcommon may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//flat_skiplist.h, support duplicated entries, and support stable sort :)
|
||||
//you should use multi_skiplist with too many duplicated entries
|
||||
|
|
@ -64,6 +71,7 @@ void *flat_skiplist_find(FlatSkiplist *sl, void *data);
|
|||
int flat_skiplist_find_all(FlatSkiplist *sl, void *data, FlatSkiplistIterator *iterator);
|
||||
int flat_skiplist_find_range(FlatSkiplist *sl, void *start_data, void *end_data,
|
||||
FlatSkiplistIterator *iterator);
|
||||
void *flat_skiplist_find_ge(FlatSkiplist *sl, void *data);
|
||||
|
||||
static inline void flat_skiplist_iterator(FlatSkiplist *sl, FlatSkiplistIterator *iterator)
|
||||
{
|
||||
|
|
@ -84,6 +92,15 @@ static inline void *flat_skiplist_next(FlatSkiplistIterator *iterator)
|
|||
return data;
|
||||
}
|
||||
|
||||
static inline void *flat_skiplist_get_first(FlatSkiplist *sl)
|
||||
{
|
||||
if (sl->top->links[0] != sl->tail) {
|
||||
return sl->top->links[0]->data;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool flat_skiplist_empty(FlatSkiplist *sl)
|
||||
{
|
||||
return sl->top->links[0] == sl->tail;
|
||||
|
|
|
|||
111
src/hash.c
111
src/hash.c
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -12,6 +19,7 @@
|
|||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include "pthread_func.h"
|
||||
#include "fc_memory.h"
|
||||
#include "hash.h"
|
||||
|
||||
static unsigned int prime_array[] = {
|
||||
|
|
@ -59,7 +67,7 @@ static int _hash_alloc_buckets(HashArray *pHash, const unsigned int old_capacity
|
|||
return ENOSPC;
|
||||
}
|
||||
|
||||
pHash->buckets = (HashData **)malloc(bytes);
|
||||
pHash->buckets = (HashData **)fc_malloc(bytes);
|
||||
if (pHash->buckets == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
|
|
@ -71,7 +79,7 @@ static int _hash_alloc_buckets(HashArray *pHash, const unsigned int old_capacity
|
|||
return 0;
|
||||
}
|
||||
|
||||
unsigned int *hash_get_prime_capacity(const int capacity)
|
||||
unsigned int *fc_hash_get_prime_capacity(const int capacity)
|
||||
{
|
||||
unsigned int *pprime;
|
||||
unsigned int *prime_end;
|
||||
|
|
@ -87,14 +95,14 @@ unsigned int *hash_get_prime_capacity(const int capacity)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
int hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
||||
int fc_hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
||||
const unsigned int capacity, const double load_factor, \
|
||||
const int64_t max_bytes, const bool bMallocValue)
|
||||
{
|
||||
int result;
|
||||
|
||||
memset(pHash, 0, sizeof(HashArray));
|
||||
pHash->capacity = hash_get_prime_capacity(capacity);
|
||||
pHash->capacity = fc_hash_get_prime_capacity(capacity);
|
||||
if (pHash->capacity == NULL)
|
||||
{
|
||||
return EINVAL;
|
||||
|
|
@ -121,7 +129,7 @@ int hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
|||
return 0;
|
||||
}
|
||||
|
||||
int hash_set_locks(HashArray *pHash, const int lock_count)
|
||||
int fc_hash_set_locks(HashArray *pHash, const int lock_count)
|
||||
{
|
||||
size_t bytes;
|
||||
pthread_mutex_t *lock;
|
||||
|
|
@ -144,7 +152,7 @@ int hash_set_locks(HashArray *pHash, const int lock_count)
|
|||
}
|
||||
|
||||
bytes = sizeof(pthread_mutex_t) * lock_count;
|
||||
pHash->locks = (pthread_mutex_t *)malloc(bytes);
|
||||
pHash->locks = (pthread_mutex_t *)fc_malloc(bytes);
|
||||
if (pHash->locks == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
|
|
@ -160,7 +168,7 @@ int hash_set_locks(HashArray *pHash, const int lock_count)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void hash_destroy(HashArray *pHash)
|
||||
void fc_hash_destroy(HashArray *pHash)
|
||||
{
|
||||
HashData **ppBucket;
|
||||
HashData **bucket_end;
|
||||
|
|
@ -230,7 +238,7 @@ void hash_destroy(HashArray *pHash)
|
|||
}
|
||||
|
||||
|
||||
int hash_stat(HashArray *pHash, HashStat *pStat, \
|
||||
int fc_hash_stat(HashArray *pHash, HashStat *pStat, \
|
||||
int *stat_by_lens, const int stat_size)
|
||||
{
|
||||
HashData **ppBucket;
|
||||
|
|
@ -291,13 +299,13 @@ int hash_stat(HashArray *pHash, HashStat *pStat, \
|
|||
return 0;
|
||||
}
|
||||
|
||||
void hash_stat_print(HashArray *pHash)
|
||||
void fc_hash_stat_print(HashArray *pHash)
|
||||
{
|
||||
#define STAT_MAX_NUM 64
|
||||
HashStat hs;
|
||||
int stats[STAT_MAX_NUM];
|
||||
|
||||
if (hash_stat(pHash, &hs, stats, STAT_MAX_NUM) != 0)
|
||||
if (fc_hash_stat(pHash, &hs, stats, STAT_MAX_NUM) != 0)
|
||||
{
|
||||
printf("hash max length exceeds %d!\n", STAT_MAX_NUM);
|
||||
return;
|
||||
|
|
@ -373,7 +381,7 @@ static int _rehash(HashArray *pHash)
|
|||
pOldCapacity = pHash->capacity;
|
||||
if (pHash->is_malloc_capacity)
|
||||
{
|
||||
pHash->capacity = hash_get_prime_capacity(*pOldCapacity);
|
||||
pHash->capacity = fc_hash_get_prime_capacity(*pOldCapacity);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -448,7 +456,7 @@ static int _hash_conflict_count(HashArray *pHash)
|
|||
return conflict_count;
|
||||
}
|
||||
|
||||
int hash_best_op(HashArray *pHash, const int suggest_capacity)
|
||||
int fc_hash_best_op(HashArray *pHash, const int suggest_capacity)
|
||||
{
|
||||
int old_capacity;
|
||||
int conflict_count;
|
||||
|
|
@ -461,7 +469,7 @@ int hash_best_op(HashArray *pHash, const int suggest_capacity)
|
|||
}
|
||||
|
||||
old_capacity = *pHash->capacity;
|
||||
new_capacity = (unsigned int *)malloc(sizeof(unsigned int));
|
||||
new_capacity = (unsigned int *)fc_malloc(sizeof(unsigned int));
|
||||
if (new_capacity == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
|
|
@ -505,7 +513,7 @@ int hash_best_op(HashArray *pHash, const int suggest_capacity)
|
|||
|
||||
pHash->is_malloc_capacity = true;
|
||||
|
||||
//hash_stat_print(pHash);
|
||||
//fc_hash_stat_print(pHash);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -529,7 +537,7 @@ static HashData *_chain_find_entry(HashData **ppBucket, const void *key, \
|
|||
return NULL;
|
||||
}
|
||||
|
||||
HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len)
|
||||
HashData *fc_hash_find_ex(HashArray *pHash, const void *key, const int key_len)
|
||||
{
|
||||
unsigned int hash_code;
|
||||
HashData **ppBucket;
|
||||
|
|
@ -545,7 +553,7 @@ HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len)
|
|||
return hash_data;
|
||||
}
|
||||
|
||||
void *hash_find(HashArray *pHash, const void *key, const int key_len)
|
||||
void *fc_hash_find(HashArray *pHash, const void *key, const int key_len)
|
||||
{
|
||||
unsigned int hash_code;
|
||||
HashData **ppBucket;
|
||||
|
|
@ -568,10 +576,10 @@ void *hash_find(HashArray *pHash, const void *key, const int key_len)
|
|||
}
|
||||
}
|
||||
|
||||
int hash_find2(HashArray *pHash, const string_t *key, string_t *value)
|
||||
int fc_hash_find2(HashArray *pHash, const string_t *key, string_t *value)
|
||||
{
|
||||
HashData *hdata;
|
||||
if ((hdata=hash_find1_ex(pHash, key)) == NULL)
|
||||
if ((hdata=fc_hash_find1_ex(pHash, key)) == NULL)
|
||||
{
|
||||
return ENOENT;
|
||||
}
|
||||
|
|
@ -581,12 +589,12 @@ int hash_find2(HashArray *pHash, const string_t *key, string_t *value)
|
|||
return 0;
|
||||
}
|
||||
|
||||
HashData *hash_find1_ex(HashArray *pHash, const string_t *key)
|
||||
HashData *fc_hash_find1_ex(HashArray *pHash, const string_t *key)
|
||||
{
|
||||
return hash_find_ex(pHash, key->str, key->len);
|
||||
return fc_hash_find_ex(pHash, key->str, key->len);
|
||||
}
|
||||
|
||||
int hash_get(HashArray *pHash, const void *key, const int key_len,
|
||||
int fc_hash_get(HashArray *pHash, const void *key, const int key_len,
|
||||
void *value, int *value_len)
|
||||
{
|
||||
unsigned int hash_code;
|
||||
|
|
@ -620,7 +628,7 @@ int hash_get(HashArray *pHash, const void *key, const int key_len,
|
|||
return result;
|
||||
}
|
||||
|
||||
int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
||||
int fc_hash_insert_ex(HashArray *pHash, const void *key, const int key_len,
|
||||
void *value, const int value_len, const bool needLock)
|
||||
{
|
||||
unsigned int hash_code;
|
||||
|
|
@ -704,7 +712,7 @@ int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
pBuff = (char *)malloc(bytes);
|
||||
pBuff = (char *)fc_malloc(bytes);
|
||||
if (pBuff == NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
|
|
@ -752,7 +760,7 @@ int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
|||
return 1;
|
||||
}
|
||||
|
||||
int64_t hash_inc_value(const HashData *old_data, const int inc,
|
||||
int64_t fc_hash_inc_value(const HashData *old_data, const int inc,
|
||||
char *new_value, int *new_value_len, void *arg)
|
||||
{
|
||||
int64_t n;
|
||||
|
|
@ -760,27 +768,26 @@ int64_t hash_inc_value(const HashData *old_data, const int inc,
|
|||
{
|
||||
if (old_data->value_len < *new_value_len)
|
||||
{
|
||||
memcpy(new_value, old_data->value, old_data->value_len);
|
||||
new_value[old_data->value_len] = '\0';
|
||||
n = strtoll(new_value, NULL, 10);
|
||||
n += inc;
|
||||
memcpy(new_value, old_data->value, old_data->value_len);
|
||||
new_value[old_data->value_len] = '\0';
|
||||
n = strtoll(new_value, NULL, 10);
|
||||
n += inc;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = inc;
|
||||
}
|
||||
*new_value_len = sprintf(new_value, "%"PRId64, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = inc;
|
||||
*new_value_len = sprintf(new_value, "%"PRId64, n);
|
||||
}
|
||||
|
||||
*new_value_len = fc_itoa(n, new_value);
|
||||
return n;
|
||||
}
|
||||
|
||||
int hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
||||
int fc_hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
||||
const int inc, char *value, int *value_len,
|
||||
ConvertValueFunc convert_func, void *arg)
|
||||
{
|
||||
|
|
@ -815,7 +822,7 @@ int hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
|||
}
|
||||
}
|
||||
}
|
||||
result = hash_insert_ex(pHash, key, key_len, value, *value_len, false);
|
||||
result = fc_hash_insert_ex(pHash, key, key_len, value, *value_len, false);
|
||||
if (result < 0)
|
||||
{
|
||||
*value = '\0';
|
||||
|
|
@ -831,7 +838,7 @@ int hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
|||
return result;
|
||||
}
|
||||
|
||||
int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
||||
int fc_hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
||||
const char *value, const int offset, const int value_len)
|
||||
{
|
||||
unsigned int hash_code;
|
||||
|
|
@ -861,7 +868,7 @@ int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
|||
break;
|
||||
}
|
||||
|
||||
pNewBuff = (char *)malloc(offset + value_len);
|
||||
pNewBuff = (char *)fc_malloc(offset + value_len);
|
||||
if (pNewBuff == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
|
|
@ -873,7 +880,7 @@ int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
|||
memcpy(pNewBuff, hash_data->value, offset);
|
||||
}
|
||||
memcpy(pNewBuff + offset, value, value_len);
|
||||
result = hash_insert_ex(pHash, key, key_len, pNewBuff,
|
||||
result = fc_hash_insert_ex(pHash, key, key_len, pNewBuff,
|
||||
offset + value_len, false);
|
||||
free(pNewBuff);
|
||||
}
|
||||
|
|
@ -884,8 +891,8 @@ int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
|||
result = ENOENT;
|
||||
break;
|
||||
}
|
||||
result = hash_insert_ex(pHash, key, key_len, (void *)value,
|
||||
value_len, false);
|
||||
result = fc_hash_insert_ex(pHash, key, key_len,
|
||||
(void *)value, value_len, false);
|
||||
}
|
||||
|
||||
if (result < 0)
|
||||
|
|
@ -902,7 +909,7 @@ int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
|||
return result;
|
||||
}
|
||||
|
||||
int hash_delete(HashArray *pHash, const void *key, const int key_len)
|
||||
int fc_hash_delete(HashArray *pHash, const void *key, const int key_len)
|
||||
{
|
||||
HashData **ppBucket;
|
||||
HashData *hash_data;
|
||||
|
|
@ -935,7 +942,7 @@ int hash_delete(HashArray *pHash, const void *key, const int key_len)
|
|||
return result;
|
||||
}
|
||||
|
||||
int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args)
|
||||
int fc_hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args)
|
||||
{
|
||||
HashData **ppBucket;
|
||||
HashData **bucket_end;
|
||||
|
|
@ -964,12 +971,12 @@ int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int hash_count(HashArray *pHash)
|
||||
int fc_hash_count(HashArray *pHash)
|
||||
{
|
||||
return pHash->item_count;
|
||||
}
|
||||
|
||||
int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index)
|
||||
int fc_hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index)
|
||||
{
|
||||
if (pHash->lock_count <= 0)
|
||||
{
|
||||
|
|
@ -980,7 +987,7 @@ int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index)
|
|||
pHash->lock_count);
|
||||
}
|
||||
|
||||
int hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index)
|
||||
int fc_hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index)
|
||||
{
|
||||
if (pHash->lock_count <= 0)
|
||||
{
|
||||
|
|
@ -1300,19 +1307,19 @@ int calc_hashnr1_ex(const void* key, const int key_len, \
|
|||
\
|
||||
h = init_value; \
|
||||
pEnd = (unsigned char *)key + key_len; \
|
||||
for (p = (unsigned char *)key; p!= pEnd; p++) \
|
||||
for (p = (unsigned char *)key; p != pEnd; p++) \
|
||||
{ \
|
||||
h = 31 * h + *p; \
|
||||
} \
|
||||
\
|
||||
return h; \
|
||||
|
||||
int simple_hash(const void* key, const int key_len)
|
||||
int fc_simple_hash(const void* key, const int key_len)
|
||||
{
|
||||
SIMPLE_HASH_FUNC(0)
|
||||
}
|
||||
|
||||
int simple_hash_ex(const void* key, const int key_len, \
|
||||
int fc_simple_hash_ex(const void* key, const int key_len, \
|
||||
const int init_value)
|
||||
{
|
||||
SIMPLE_HASH_FUNC(init_value)
|
||||
|
|
|
|||
89
src/hash.h
89
src/hash.h
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _HASH_H_
|
||||
#define _HASH_H_
|
||||
|
|
@ -86,16 +93,16 @@ typedef struct tagHashStat
|
|||
* parameters:
|
||||
* index: item index based 0
|
||||
* data: hash data, including key and value
|
||||
* args: passed by hash_walk function
|
||||
* args: passed by fc_hash_walk function
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
typedef int (*HashWalkFunc)(const int index, const HashData *data, void *args);
|
||||
|
||||
#define hash_init(pHash, hash_func, capacity, load_factor) \
|
||||
hash_init_ex(pHash, hash_func, capacity, load_factor, 0, false)
|
||||
#define fc_hash_init(pHash, hash_func, capacity, load_factor) \
|
||||
fc_hash_init_ex(pHash, hash_func, capacity, load_factor, 0, false)
|
||||
|
||||
#define hash_insert(pHash, key, key_len, value) \
|
||||
hash_insert_ex(pHash, key, key_len, value, 0, true)
|
||||
#define fc_hash_insert(pHash, key, key_len, value) \
|
||||
fc_hash_insert_ex(pHash, key, key_len, value, 0, true)
|
||||
|
||||
/**
|
||||
* hash init function
|
||||
|
|
@ -108,7 +115,7 @@ typedef int (*HashWalkFunc)(const int index, const HashData *data, void *args);
|
|||
* bMallocValue: if need malloc value buffer
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
int hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
||||
int fc_hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
||||
const unsigned int capacity, const double load_factor, \
|
||||
const int64_t max_bytes, const bool bMallocValue);
|
||||
|
||||
|
|
@ -118,7 +125,7 @@ int hash_init_ex(HashArray *pHash, HashFunc hash_func, \
|
|||
* lock_count: the lock count
|
||||
* return 0 for success, != 0 for error
|
||||
*/
|
||||
int hash_set_locks(HashArray *pHash, const int lock_count);
|
||||
int fc_hash_set_locks(HashArray *pHash, const int lock_count);
|
||||
|
||||
/**
|
||||
* convert the value
|
||||
|
|
@ -130,12 +137,12 @@ int hash_set_locks(HashArray *pHash, const int lock_count);
|
|||
* arg: the user data
|
||||
* return the number after increasement
|
||||
*/
|
||||
int64_t hash_inc_value(const HashData *old_data, const int inc,
|
||||
int64_t fc_hash_inc_value(const HashData *old_data, const int inc,
|
||||
char *new_value, int *new_value_len, void *arg);
|
||||
|
||||
#define hash_inc(pHash, key, key_len, inc, value, value_len) \
|
||||
hash_inc_ex(pHash, key, key_len, inc, value, value_len, \
|
||||
hash_inc_value, NULL)
|
||||
#define fc_hash_inc(pHash, key, key_len, inc, value, value_len) \
|
||||
fc_hash_inc_ex(pHash, key, key_len, inc, value, value_len, \
|
||||
fc_hash_inc_value, NULL)
|
||||
|
||||
/**
|
||||
* atomic increase value
|
||||
|
|
@ -151,7 +158,7 @@ int64_t hash_inc_value(const HashData *old_data, const int inc,
|
|||
* return 0 for success, != 0 for error (errno)
|
||||
*
|
||||
*/
|
||||
int hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
||||
int fc_hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
||||
const int inc, char *value, int *value_len,
|
||||
ConvertValueFunc convert_func, void *arg);
|
||||
|
||||
|
|
@ -161,7 +168,7 @@ int hash_inc_ex(HashArray *pHash, const void *key, const int key_len,
|
|||
* pHash: the hash table
|
||||
* return none
|
||||
*/
|
||||
void hash_destroy(HashArray *pHash);
|
||||
void fc_hash_destroy(HashArray *pHash);
|
||||
|
||||
/**
|
||||
* hash insert key
|
||||
|
|
@ -175,7 +182,7 @@ void hash_destroy(HashArray *pHash);
|
|||
* return >= 0 for success, 0 for key already exist (update),
|
||||
* 1 for new key (insert), < 0 for error
|
||||
*/
|
||||
int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
||||
int fc_hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
||||
void *value, const int value_len, const bool needLock);
|
||||
|
||||
/**
|
||||
|
|
@ -186,7 +193,7 @@ int hash_insert_ex(HashArray *pHash, const void *key, const int key_len, \
|
|||
* key_len: length of th key
|
||||
* return user data, return NULL when the key not exist
|
||||
*/
|
||||
void *hash_find(HashArray *pHash, const void *key, const int key_len);
|
||||
void *fc_hash_find(HashArray *pHash, const void *key, const int key_len);
|
||||
|
||||
/**
|
||||
* hash find key
|
||||
|
|
@ -196,7 +203,7 @@ void *hash_find(HashArray *pHash, const void *key, const int key_len);
|
|||
* key_len: length of th key
|
||||
* return hash data, return NULL when the key not exist
|
||||
*/
|
||||
HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len);
|
||||
HashData *fc_hash_find_ex(HashArray *pHash, const void *key, const int key_len);
|
||||
|
||||
/**
|
||||
* hash find key
|
||||
|
|
@ -205,9 +212,9 @@ HashData *hash_find_ex(HashArray *pHash, const void *key, const int key_len);
|
|||
* key: the key to find
|
||||
* return user data, return NULL when the key not exist
|
||||
*/
|
||||
static inline void *hash_find1(HashArray *pHash, const string_t *key)
|
||||
static inline void *fc_hash_find1(HashArray *pHash, const string_t *key)
|
||||
{
|
||||
return hash_find(pHash, key->str, key->len);
|
||||
return fc_hash_find(pHash, key->str, key->len);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -218,7 +225,7 @@ static inline void *hash_find1(HashArray *pHash, const string_t *key)
|
|||
* value: store the value
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_find2(HashArray *pHash, const string_t *key, string_t *value);
|
||||
int fc_hash_find2(HashArray *pHash, const string_t *key, string_t *value);
|
||||
|
||||
/**
|
||||
* hash find key
|
||||
|
|
@ -227,7 +234,7 @@ int hash_find2(HashArray *pHash, const string_t *key, string_t *value);
|
|||
* key: the key to find
|
||||
* return hash data, return NULL when the key not exist
|
||||
*/
|
||||
HashData *hash_find1_ex(HashArray *pHash, const string_t *key);
|
||||
HashData *fc_hash_find1_ex(HashArray *pHash, const string_t *key);
|
||||
|
||||
/**
|
||||
* hash get the value of the key
|
||||
|
|
@ -240,7 +247,7 @@ HashData *hash_find1_ex(HashArray *pHash, const string_t *key);
|
|||
* output for the length fo the value
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_get(HashArray *pHash, const void *key, const int key_len,
|
||||
int fc_hash_get(HashArray *pHash, const void *key, const int key_len,
|
||||
void *value, int *value_len);
|
||||
|
||||
|
||||
|
|
@ -255,7 +262,7 @@ int hash_get(HashArray *pHash, const void *key, const int key_len,
|
|||
* value_len: length of the value
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
||||
int fc_hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
||||
const char *value, const int offset, const int value_len);
|
||||
|
||||
/**
|
||||
|
|
@ -266,7 +273,7 @@ int hash_partial_set(HashArray *pHash, const void *key, const int key_len,
|
|||
* key_len: length of th key
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_delete(HashArray *pHash, const void *key, const int key_len);
|
||||
int fc_hash_delete(HashArray *pHash, const void *key, const int key_len);
|
||||
|
||||
/**
|
||||
* hash walk (iterator)
|
||||
|
|
@ -276,7 +283,7 @@ int hash_delete(HashArray *pHash, const void *key, const int key_len);
|
|||
* args: extra args which will be passed to walkFunc
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args);
|
||||
int fc_hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args);
|
||||
|
||||
/**
|
||||
* get hash item count
|
||||
|
|
@ -284,7 +291,7 @@ int hash_walk(HashArray *pHash, HashWalkFunc walkFunc, void *args);
|
|||
* pHash: the hash table
|
||||
* return item count
|
||||
*/
|
||||
int hash_count(HashArray *pHash);
|
||||
int fc_hash_count(HashArray *pHash);
|
||||
|
||||
/**
|
||||
* hash best optimize
|
||||
|
|
@ -293,7 +300,7 @@ int hash_count(HashArray *pHash);
|
|||
* suggest_capacity: suggest init capacity for speed
|
||||
* return >0 for success, < 0 fail (errno)
|
||||
*/
|
||||
int hash_best_op(HashArray *pHash, const int suggest_capacity);
|
||||
int fc_hash_best_op(HashArray *pHash, const int suggest_capacity);
|
||||
|
||||
/**
|
||||
* hash stat
|
||||
|
|
@ -307,7 +314,7 @@ int hash_best_op(HashArray *pHash, const int suggest_capacity);
|
|||
* stat_size: stats array size (contain max elments)
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_stat(HashArray *pHash, HashStat *pStat, \
|
||||
int fc_hash_stat(HashArray *pHash, HashStat *pStat, \
|
||||
int *stat_by_lens, const int stat_size);
|
||||
|
||||
/**
|
||||
|
|
@ -316,7 +323,7 @@ int hash_stat(HashArray *pHash, HashStat *pStat, \
|
|||
* pHash: the hash table
|
||||
* return none
|
||||
*/
|
||||
void hash_stat_print(HashArray *pHash);
|
||||
void fc_hash_stat_print(HashArray *pHash);
|
||||
|
||||
/**
|
||||
* lock the bucket of hash table
|
||||
|
|
@ -325,7 +332,7 @@ void hash_stat_print(HashArray *pHash);
|
|||
* bucket_index: the index of bucket
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index);
|
||||
int fc_hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index);
|
||||
|
||||
/**
|
||||
* unlock the bucket of hash table
|
||||
|
|
@ -334,7 +341,7 @@ int hash_bucket_lock(HashArray *pHash, const unsigned int bucket_index);
|
|||
* bucket_index: the index of bucket
|
||||
* return 0 for success, != 0 fail (errno)
|
||||
*/
|
||||
int hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index);
|
||||
int fc_hash_bucket_unlock(HashArray *pHash, const unsigned int bucket_index);
|
||||
|
||||
int RSHash(const void *key, const int key_len);
|
||||
|
||||
|
|
@ -376,8 +383,8 @@ int calc_hashnr1(const void* key, const int key_len);
|
|||
int calc_hashnr1_ex(const void* key, const int key_len, \
|
||||
const int init_value);
|
||||
|
||||
int simple_hash(const void* key, const int key_len);
|
||||
int simple_hash_ex(const void* key, const int key_len, \
|
||||
int fc_simple_hash(const void* key, const int key_len);
|
||||
int fc_simple_hash_ex(const void* key, const int key_len, \
|
||||
const int init_value);
|
||||
|
||||
int CRC32(const void *key, const int key_len);
|
||||
|
|
@ -395,7 +402,7 @@ int64_t CRC32_ex(const void *key, const int key_len, \
|
|||
#define CALC_HASH_CODES4(buff, buff_len, hash_codes) \
|
||||
hash_codes[0] = CRC32_ex(buff, buff_len, hash_codes[0]); \
|
||||
hash_codes[1] = ELFHash_ex(buff, buff_len, hash_codes[1]); \
|
||||
hash_codes[2] = simple_hash_ex(buff, buff_len, hash_codes[2]); \
|
||||
hash_codes[2] = fc_simple_hash_ex(buff, buff_len, hash_codes[2]); \
|
||||
hash_codes[3] = Time33Hash_ex(buff, buff_len, hash_codes[3]); \
|
||||
|
||||
|
||||
|
|
@ -403,7 +410,7 @@ int64_t CRC32_ex(const void *key, const int key_len, \
|
|||
hash_codes[0] = CRC32_FINAL(hash_codes[0]); \
|
||||
|
||||
|
||||
unsigned int *hash_get_prime_capacity(const int capacity);
|
||||
unsigned int *fc_hash_get_prime_capacity(const int capacity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
184
src/http_func.c
184
src/http_func.c
|
|
@ -1,10 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
* Please visit the FastDFS Home Page http://www.fastken.com/ for more detail.
|
||||
**/
|
||||
|
||||
#include <time.h>
|
||||
|
|
@ -21,8 +36,134 @@
|
|||
#include "sockopt.h"
|
||||
#include "logger.h"
|
||||
#include "shared_func.h"
|
||||
#include "fc_memory.h"
|
||||
#include "http_func.h"
|
||||
|
||||
#ifdef USE_LIBCURL
|
||||
#include <curl/curl.h>
|
||||
|
||||
static bool curl_inited = false;
|
||||
|
||||
typedef struct {
|
||||
char *buff;
|
||||
int length;
|
||||
int alloc_size;
|
||||
bool dynamic_alloc;
|
||||
} CurlCallbackArg;
|
||||
|
||||
static size_t curl_write_data(void *ptr, size_t size,
|
||||
size_t nmemb, void *userdata)
|
||||
{
|
||||
size_t len;
|
||||
int alloc_size;
|
||||
char *new_buff;
|
||||
CurlCallbackArg *cbarg;
|
||||
|
||||
cbarg = (CurlCallbackArg *)userdata;
|
||||
len = size * nmemb;
|
||||
if ((cbarg->alloc_size - cbarg->length) < len) {
|
||||
if (!cbarg->dynamic_alloc) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
alloc_size = 2 * cbarg->alloc_size;
|
||||
while ((alloc_size - cbarg->length) < len) {
|
||||
alloc_size *= 2;
|
||||
}
|
||||
|
||||
new_buff = (char *)fc_malloc(alloc_size);
|
||||
if (new_buff == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cbarg->length > 0) {
|
||||
memcpy(new_buff, cbarg->buff, cbarg->length);
|
||||
}
|
||||
free(cbarg->buff);
|
||||
|
||||
cbarg->buff = new_buff;
|
||||
cbarg->alloc_size = alloc_size;
|
||||
}
|
||||
|
||||
memcpy(cbarg->buff + cbarg->length, ptr, len);
|
||||
cbarg->length += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
int get_url_content_ex(const char *url, const int url_len,
|
||||
const int connect_timeout, const int network_timeout,
|
||||
int *http_status, char **content, int *content_len,
|
||||
char *error_info)
|
||||
{
|
||||
CURLcode result;
|
||||
long response_code;
|
||||
CURL *curl;
|
||||
CurlCallbackArg cbarg;
|
||||
|
||||
*error_info = '\0';
|
||||
*http_status = 0;
|
||||
if (!curl_inited) {
|
||||
if ((result=curl_global_init(CURL_GLOBAL_ALL)) != 0) {
|
||||
sprintf(error_info, "curl_global_init fail "
|
||||
"with code: %d", result);
|
||||
return errno != 0 ? errno : EBUSY;
|
||||
}
|
||||
curl_inited = true;
|
||||
}
|
||||
|
||||
if ((curl=curl_easy_init()) == NULL) {
|
||||
sprintf(error_info, "curl_easy_init fail");
|
||||
return errno != 0 ? errno : EBUSY;
|
||||
}
|
||||
|
||||
if (*content == NULL) {
|
||||
cbarg.dynamic_alloc = true;
|
||||
cbarg.alloc_size = 16 * 1024;
|
||||
cbarg.buff = (char *)fc_malloc(cbarg.alloc_size);
|
||||
if (cbarg.buff == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
} else {
|
||||
cbarg.dynamic_alloc = false;
|
||||
cbarg.alloc_size = *content_len;
|
||||
cbarg.buff = *content;
|
||||
}
|
||||
cbarg.length = 0;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, connect_timeout);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, connect_timeout + network_timeout);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cbarg);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
|
||||
|
||||
result = curl_easy_perform(curl);
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
*http_status = response_code;
|
||||
if (result == CURLE_OK) {
|
||||
if (cbarg.dynamic_alloc) {
|
||||
*content = cbarg.buff;
|
||||
}
|
||||
*content_len = cbarg.length;
|
||||
*(*content + *content_len) = '\0';
|
||||
return 0;
|
||||
} else {
|
||||
sprintf(error_info, "curl_easy_perform fail with code: %d, %s",
|
||||
result, curl_easy_strerror(result));
|
||||
if (cbarg.dynamic_alloc && cbarg.buff != NULL) {
|
||||
free(cbarg.buff);
|
||||
}
|
||||
*content_len = 0;
|
||||
return EACCES;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int get_url_content_ex(const char *url, const int url_len,
|
||||
const int connect_timeout, const int network_timeout,
|
||||
int *http_status, char **content, int *content_len, char *error_info)
|
||||
|
|
@ -44,6 +185,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
char *pPort;
|
||||
char *pSpace;
|
||||
|
||||
*error_info = '\0';
|
||||
*http_status = 0;
|
||||
if (*content == NULL)
|
||||
{
|
||||
|
|
@ -130,7 +272,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
close(sock);
|
||||
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"connect to %s:%d fail, errno: %d, " \
|
||||
"connect to %s:%u fail, errno: %d, " \
|
||||
"error info: %s", __LINE__, domain_name, \
|
||||
port, result, STRERROR(result));
|
||||
|
||||
|
|
@ -139,7 +281,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
|
||||
out_len = snprintf(out_buff, sizeof(out_buff), \
|
||||
"GET %s HTTP/1.0\r\n" \
|
||||
"Host: %s:%d\r\n" \
|
||||
"Host: %s:%u\r\n" \
|
||||
"Connection: close\r\n" \
|
||||
"\r\n", pURI, domain_name, port);
|
||||
if ((result=tcpsenddata(sock, out_buff, out_len, network_timeout)) != 0)
|
||||
|
|
@ -147,7 +289,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
close(sock);
|
||||
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"send data to %s:%d fail, errno: %d, " \
|
||||
"send data to %s:%u fail, errno: %d, " \
|
||||
"error info: %s", __LINE__, domain_name, \
|
||||
port, result, STRERROR(result));
|
||||
|
||||
|
|
@ -156,18 +298,11 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
|
||||
if (bNeedAlloc)
|
||||
{
|
||||
*content = (char *)malloc(alloc_size + 1);
|
||||
*content = (char *)fc_malloc(alloc_size + 1);
|
||||
if (*content == NULL)
|
||||
{
|
||||
close(sock);
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes fail, errno: %d, " \
|
||||
"error info: %s", __LINE__, alloc_size + 1, \
|
||||
result, STRERROR(result));
|
||||
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,20 +314,12 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
if (bNeedAlloc)
|
||||
{
|
||||
alloc_size *= 2;
|
||||
*content = (char *)realloc(*content, alloc_size + 1);
|
||||
*content = (char *)fc_realloc(*content, alloc_size + 1);
|
||||
if (*content == NULL)
|
||||
{
|
||||
*content_len = 0;
|
||||
close(sock);
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"realloc %d bytes fail, errno: %d, " \
|
||||
"error info: %s", __LINE__, \
|
||||
alloc_size + 1, \
|
||||
result, STRERROR(result));
|
||||
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
recv_bytes = alloc_size - *content_len;
|
||||
|
|
@ -220,7 +347,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
}
|
||||
else {
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"recv data from %s:%d fail, errno: %d, " \
|
||||
"recv data from %s:%u fail, errno: %d, " \
|
||||
"error info: %s", __LINE__, domain_name, \
|
||||
port, result, STRERROR(result));
|
||||
|
||||
|
|
@ -232,7 +359,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
if (pContent == NULL)
|
||||
{
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"response data from %s:%d is invalid", \
|
||||
"response data from %s:%u is invalid", \
|
||||
__LINE__, domain_name, port);
|
||||
|
||||
result = EINVAL;
|
||||
|
|
@ -244,7 +371,7 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
if (pSpace == NULL || pSpace >= pContent)
|
||||
{
|
||||
sprintf(error_info, "file: "__FILE__", line: %d, " \
|
||||
"response data from %s:%d is invalid", \
|
||||
"response data from %s:%u is invalid", \
|
||||
__LINE__, domain_name, port);
|
||||
|
||||
result = EINVAL;
|
||||
|
|
@ -253,9 +380,8 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
|
||||
*http_status = atoi(pSpace + 1);
|
||||
*content_len -= pContent - *content;
|
||||
memcpy(*content, pContent, *content_len);
|
||||
memmove(*content, pContent, *content_len);
|
||||
*(*content + *content_len) = '\0';
|
||||
*error_info = '\0';
|
||||
} while (0);
|
||||
|
||||
close(sock);
|
||||
|
|
@ -269,6 +395,8 @@ int get_url_content_ex(const char *url, const int url_len,
|
|||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int get_url_content(const char *url, const int connect_timeout, \
|
||||
const int network_timeout, int *http_status, \
|
||||
char **content, int *content_len, char *error_info)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _HTTP_FUNC_H
|
||||
#define _HTTP_FUNC_H
|
||||
|
|
@ -16,6 +23,14 @@
|
|||
#include <ctype.h>
|
||||
#include "common_define.h"
|
||||
|
||||
#define IS_URL_RESOURCE(str) \
|
||||
((strncasecmp(str, "http://", 7) == 0) || \
|
||||
(strncasecmp(str, "https://", 8) == 0))
|
||||
|
||||
#define IS_URL_RESOURCE_EX(s) \
|
||||
((s)->len >= 8 && ((memcmp((s)->str, "http://", 7) == 0) || \
|
||||
(memcmp((s)->str, "https://", 8) == 0)))
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -77,6 +84,7 @@ int id_generator_init_extra_ex(struct idg_context *context, const char *filename
|
|||
const char *local_ip;
|
||||
const char *private_ip;
|
||||
struct in_addr ip_addr;
|
||||
struct in6_addr ip6_addr;
|
||||
|
||||
private_ip = get_first_local_private_ip();
|
||||
if (private_ip != NULL)
|
||||
|
|
@ -93,16 +101,28 @@ int id_generator_init_extra_ex(struct idg_context *context, const char *filename
|
|||
context->fd = -1;
|
||||
return ENOENT;
|
||||
}
|
||||
else if (strcmp(local_ip, LOCAL_LOOPBACK_IP) == 0)
|
||||
else if (strcmp(local_ip, LOCAL_LOOPBACK_IPv4) == 0)
|
||||
{
|
||||
/* 注意,当系统存在IPv6回环地址时,为了简化系统的改动,
|
||||
会将IPv6回环地址修改成IPv4回环地址返回
|
||||
此处错误打印时,需要带上IPv6回环地址
|
||||
*/
|
||||
logWarning("file: "__FILE__", line: %d, "
|
||||
"can't get local ip address, set to %s",
|
||||
__LINE__, LOCAL_LOOPBACK_IP);
|
||||
"can't get local ip address, set to %s or %s",
|
||||
__LINE__, LOCAL_LOOPBACK_IPv4, LOCAL_LOOPBACK_IPv6);
|
||||
}
|
||||
}
|
||||
|
||||
if (inet_pton(AF_INET, local_ip, &ip_addr) != 1)
|
||||
{
|
||||
if (inet_pton(AF_INET, local_ip, &ip_addr) == 1)
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
else if(inet_pton(AF_INET6, local_ip, &ip6_addr) == 1)
|
||||
{
|
||||
ip_addr.s_addr = *((in_addr_64_t *)&ip6_addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid local ip address: %s",
|
||||
__LINE__, local_ip);
|
||||
|
|
@ -116,13 +136,14 @@ int id_generator_init_extra_ex(struct idg_context *context, const char *filename
|
|||
mid = ntohl(ip_addr.s_addr) & ((1 << mid_bits) - 1);
|
||||
}
|
||||
|
||||
if ((context->fd = open(filename, O_RDWR)) < 0)
|
||||
if ((context->fd = open(filename, O_RDWR | O_CLOEXEC)) < 0)
|
||||
{
|
||||
if (errno == ENOENT)
|
||||
{
|
||||
mode_t old_mode;
|
||||
old_mode = umask(0);
|
||||
if ((context->fd=open(filename, O_RDWR | O_CREAT, mode)) < 0)
|
||||
if ((context->fd=open(filename, O_RDWR | O_CREAT |
|
||||
O_CLOEXEC, mode)) < 0)
|
||||
{
|
||||
result = errno != 0 ? errno : EACCES;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2016 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
64 bits id generator for multi processes, the generated id format:
|
||||
|
|
@ -19,7 +26,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <inttypes.h>
|
||||
#include <fcntl.h>
|
||||
#include "common_define.h"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//ini_file_reader.h
|
||||
#ifndef INI_FILE_READER_H
|
||||
|
|
@ -16,8 +23,8 @@
|
|||
#include "common_define.h"
|
||||
#include "hash.h"
|
||||
|
||||
#define FAST_INI_ITEM_NAME_LEN 64
|
||||
#define FAST_INI_ITEM_VALUE_LEN 256
|
||||
#define FAST_INI_ITEM_NAME_LEN 63
|
||||
#define FAST_INI_ITEM_VALUE_LEN 255
|
||||
#define FAST_INI_ITEM_NAME_SIZE (FAST_INI_ITEM_NAME_LEN + 1)
|
||||
#define FAST_INI_ITEM_VALUE_SIZE (FAST_INI_ITEM_VALUE_LEN + 1)
|
||||
|
||||
|
|
@ -26,8 +33,22 @@
|
|||
#define FAST_INI_ANNOTATION_WITH_BUILTIN 2
|
||||
#define FAST_INI_ANNOTATION_NONE FAST_INI_ANNOTATION_DISABLE
|
||||
|
||||
#define FAST_INI_FLAGS_NONE 0
|
||||
#define FAST_INI_FLAGS_SHELL_EXECUTE 1
|
||||
#define FAST_INI_FLAGS_NONE 0
|
||||
#define FAST_INI_FLAGS_SHELL_EXECUTE 1
|
||||
#define FAST_INI_FLAGS_DISABLE_SAME_SECTION_MERGE 2
|
||||
|
||||
typedef bool (*IniSectionNameFilterFunc)(const char *section_name,
|
||||
const int name_len, void *args);
|
||||
|
||||
#define FAST_INI_SET_FULL_CTX_EX(ctx, config_file, sname, ini_context) \
|
||||
do { \
|
||||
ctx.filename = config_file; \
|
||||
ctx.section_name = sname; \
|
||||
ctx.context = ini_context; \
|
||||
} while (0)
|
||||
|
||||
#define FAST_INI_SET_FULL_CTX(ctx, config_file, sname) \
|
||||
FAST_INI_SET_FULL_CTX_EX(ctx, config_file, sname, NULL)
|
||||
|
||||
typedef struct ini_item
|
||||
{
|
||||
|
|
@ -39,7 +60,7 @@ typedef struct ini_section
|
|||
{
|
||||
IniItem *items;
|
||||
int count; //item count
|
||||
int alloc_count;
|
||||
int alloc;
|
||||
} IniSection;
|
||||
|
||||
typedef struct ini_section_info
|
||||
|
|
@ -58,6 +79,13 @@ typedef struct ini_context
|
|||
char flags;
|
||||
} IniContext;
|
||||
|
||||
typedef struct ini_full_context
|
||||
{
|
||||
const char *filename;
|
||||
const char *section_name;
|
||||
IniContext *context;
|
||||
} IniFullContext;
|
||||
|
||||
typedef struct ini_annotation_entry {
|
||||
char *func_name;
|
||||
void *arg;
|
||||
|
|
@ -86,6 +114,61 @@ extern "C" {
|
|||
strcasecmp(pValue, "on") == 0 || \
|
||||
strcmp(pValue, "1") == 0)
|
||||
|
||||
#define iniGetStrValue(szSectionName, szItemName, pContext) \
|
||||
iniGetStrValueEx(szSectionName, szItemName, pContext, false)
|
||||
|
||||
#define iniGetCharValue(szSectionName, szItemName, pContext, cDefaultValue) \
|
||||
iniGetCharValueEx(szSectionName, szItemName, \
|
||||
pContext, cDefaultValue, false)
|
||||
|
||||
#define iniGetIntValue(szSectionName, szItemName, pContext, nDefaultValue) \
|
||||
iniGetIntValueEx(szSectionName, szItemName, \
|
||||
pContext, nDefaultValue, false)
|
||||
|
||||
#define iniGetInt64Value(szSectionName, szItemName, pContext, nDefaultValue) \
|
||||
iniGetInt64ValueEx(szSectionName, szItemName, \
|
||||
pContext, nDefaultValue, false)
|
||||
|
||||
#define iniGetDoubleValue(szSectionName, szItemName, pContext, dbDefaultValue) \
|
||||
iniGetDoubleValueEx(szSectionName, szItemName, \
|
||||
pContext, dbDefaultValue, false)
|
||||
|
||||
#define iniGetBoolValue(szSectionName, szItemName, pContext, bDefaultValue) \
|
||||
iniGetBoolValueEx(szSectionName, szItemName, \
|
||||
pContext, bDefaultValue, false)
|
||||
|
||||
#define iniGetPercentValue(ini_ctx, item_name, item_value, default_value) \
|
||||
iniGetPercentValueEx(ini_ctx, item_name, item_value, default_value, false)
|
||||
|
||||
#define iniGetByteValue(szSectionName, szItemName, pContext, nDefaultValue) \
|
||||
iniGetByteValueEx(szSectionName, szItemName, \
|
||||
pContext, nDefaultValue, 1, false)
|
||||
|
||||
#define iniGetIntCorrectValue(ini_ctx, item_name, \
|
||||
default_value, min_value, max_value) \
|
||||
iniGetIntCorrectValueEx(ini_ctx, item_name, \
|
||||
default_value, min_value, max_value, false)
|
||||
|
||||
#define iniGetInt64CorrectValue(ini_ctx, item_name, \
|
||||
default_value, min_value, max_value) \
|
||||
iniGetInt64CorrectValueEx(ini_ctx, item_name, \
|
||||
default_value, min_value, max_value, false)
|
||||
|
||||
#define iniGetByteCorrectValue(ini_ctx, item_name, \
|
||||
default_value, min_value, max_value) \
|
||||
iniGetByteCorrectValueEx(ini_ctx, item_name, \
|
||||
default_value, 1, min_value, max_value, false)
|
||||
|
||||
#define iniGetDoubleCorrectValue(ini_ctx, item_name, \
|
||||
default_value, min_value, max_value) \
|
||||
iniGetDoubleCorrectValueEx(ini_ctx, item_name, \
|
||||
default_value, 1, min_value, max_value, false)
|
||||
|
||||
#define iniGetPercentCorrectValue(ini_ctx, item_name, \
|
||||
item_value, default_value, min_value, max_value) \
|
||||
iniGetPercentCorrectValueEx(ini_ctx, item_name, item_value, \
|
||||
default_value, min_value, max_value, false)
|
||||
|
||||
int iniSetAnnotationCallBack(AnnotationEntry *annotations, int count);
|
||||
void iniDestroyAnnotationCallBack();
|
||||
|
||||
|
|
@ -100,6 +183,16 @@ void iniAnnotationFreeValues(struct ini_annotation_entry *annotation,
|
|||
*/
|
||||
int iniLoadFromFile(const char *szFilename, IniContext *pContext);
|
||||
|
||||
/** load ini items from file with flags
|
||||
* parameters:
|
||||
* szFilename: the filename, can be an URL
|
||||
* pContext: the ini context
|
||||
* flags: the flags
|
||||
* return: error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int iniLoadFromFile1(const char *szFilename,
|
||||
IniContext *pContext, const char flags);
|
||||
|
||||
/** load ini items from file
|
||||
* parameters:
|
||||
* szFilename: the filename, can be an URL
|
||||
|
|
@ -136,6 +229,15 @@ int iniLoadFromBufferEx(char *content, IniContext *pContext,
|
|||
const char annotation_type, AnnotationEntry *annotations, const int count,
|
||||
const char flags);
|
||||
|
||||
/** load ini items from string buffer with flags
|
||||
* parameters:
|
||||
* content: the string buffer to parse
|
||||
* pContext: the ini context
|
||||
* flags: the flags
|
||||
* return: error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int iniLoadFromBuffer1(char *content, IniContext *pContext, const char flags);
|
||||
|
||||
/** free ini context
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
|
|
@ -145,18 +247,33 @@ void iniFreeContext(IniContext *pContext);
|
|||
|
||||
/** get item string value
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: item value, return NULL when the item not exist
|
||||
*/
|
||||
char *iniGetStrValue(const char *szSectionName, const char *szItemName, \
|
||||
IniContext *pContext);
|
||||
char *iniGetStrValueEx(const char *szSectionName, const char *szItemName,
|
||||
IniContext *pContext, const bool bRetryGlobal);
|
||||
|
||||
/** get the first charactor
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* cDefaultValue: the default value
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: item value, return default value when the item not exist
|
||||
*/
|
||||
char iniGetCharValueEx(const char *szSectionName, const char *szItemName,
|
||||
IniContext *pContext, const char cDefaultValue,
|
||||
const bool bRetryGlobal);
|
||||
|
||||
/** get item string value
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
|
|
@ -169,63 +286,215 @@ int iniGetValues(const char *szSectionName, const char *szItemName, \
|
|||
|
||||
/** get item int value (32 bits)
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* nDefaultValue: the default value
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: item value, return nDefaultValue when the item not exist
|
||||
*/
|
||||
int iniGetIntValue(const char *szSectionName, const char *szItemName, \
|
||||
IniContext *pContext, const int nDefaultValue);
|
||||
int iniGetIntValueEx(const char *szSectionName,
|
||||
const char *szItemName, IniContext *pContext,
|
||||
const int nDefaultValue, const bool bRetryGlobal);
|
||||
|
||||
/** check and correct int value
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* nValue: the item value
|
||||
* nMinValue: the min value to check (including)
|
||||
* nMaxValue: the max value to check (including)
|
||||
* return: corrected value
|
||||
*/
|
||||
int64_t iniCheckAndCorrectIntValue(IniFullContext *pIniContext,
|
||||
const char *szItemName, const int64_t nValue,
|
||||
const int64_t nMinValue, const int64_t nMaxValue);
|
||||
|
||||
/** check and correct double value
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* dValue: the item value
|
||||
* dMinValue: the min value to check (including)
|
||||
* dMaxValue: the max value to check (including)
|
||||
* return: corrected value
|
||||
*/
|
||||
double iniCheckAndCorrectDoubleValue(IniFullContext *pIniContext,
|
||||
const char *szItemName, const double dValue,
|
||||
const double dMinValue, const double dMaxValue);
|
||||
|
||||
/** check and correct double value (show as percentage)
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* dValue: the item value
|
||||
* dMinValue: the min value to check (including)
|
||||
* dMaxValue: the max value to check (including)
|
||||
* return: corrected value
|
||||
*/
|
||||
double iniCheckAndCorrectPercentValue(IniFullContext *pIniContext,
|
||||
const char *szItemName, const double dValue,
|
||||
const double dMinValue, const double dMaxValue);
|
||||
|
||||
/** get item correct value (32 bits integer)
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* nDefaultValue: the default value
|
||||
* nMinValue: the min value to check (including)
|
||||
* nMaxValue: the max value to check (including)
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: item value, return nDefaultValue when the item not exist
|
||||
*/
|
||||
int iniGetIntCorrectValueEx(IniFullContext *pIniContext,
|
||||
const char *szItemName, const int nDefaultValue,
|
||||
const int nMinValue, const int nMaxValue, const bool bRetryGlobal);
|
||||
|
||||
/** get item string value array
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* nTargetCount: store the item value count
|
||||
* return: item value array, return NULL when the item not exist
|
||||
*/
|
||||
IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName, \
|
||||
IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName,
|
||||
IniContext *pContext, int *nTargetCount);
|
||||
|
||||
/** get item int64 value (64 bits)
|
||||
/** get item value (64 bits integer)
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* nDefaultValue: the default value
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: int64 value, return nDefaultValue when the item not exist
|
||||
*/
|
||||
int64_t iniGetInt64Value(const char *szSectionName, const char *szItemName, \
|
||||
IniContext *pContext, const int64_t nDefaultValue);
|
||||
int64_t iniGetInt64ValueEx(const char *szSectionName,
|
||||
const char *szItemName, IniContext *pContext,
|
||||
const int64_t nDefaultValue, const bool bRetryGlobal);
|
||||
|
||||
/** get item correct value (64 bits integer)
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* nDefaultValue: the default value
|
||||
* nMinValue: the min value to check (including)
|
||||
* nMaxValue: the max value to check (including)
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: int64 value, return nDefaultValue when the item not exist
|
||||
*/
|
||||
int64_t iniGetInt64CorrectValueEx(IniFullContext *pIniContext,
|
||||
const char *szItemName, const int64_t nDefaultValue,
|
||||
const int64_t nMinValue, const int64_t nMaxValue,
|
||||
const bool bRetryGlobal);
|
||||
|
||||
/** get item byte value (64 bits integer)
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* nDefaultValue: the default value
|
||||
* nDefaultUnitBytes: the default byte unit, such as 1 for byte, 1024 for KB
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: int64 value, return nDefaultValue when the item not exist
|
||||
*/
|
||||
int64_t iniGetByteValueEx(const char *szSectionName,
|
||||
const char *szItemName, IniContext *pContext,
|
||||
const int64_t nDefaultValue, const int nDefaultUnitBytes,
|
||||
const bool bRetryGlobal);
|
||||
|
||||
/** get item correct byte value (64 bits integer)
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* nDefaultValue: the default value
|
||||
* nDefaultUnitBytes: the default byte unit, such as 1 for byte, 1024 for KB
|
||||
* nMinValue: the min value to check (including)
|
||||
* nMaxValue: the max value to check (including)
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: int64 value, return nDefaultValue when the item not exist
|
||||
*/
|
||||
int64_t iniGetByteCorrectValueEx(IniFullContext *pIniContext,
|
||||
const char *szItemName, const int64_t nDefaultValue,
|
||||
const int nDefaultUnitBytes, const int64_t nMinValue,
|
||||
const int64_t nMaxValue, const bool bRetryGlobal);
|
||||
|
||||
/** get item boolean value
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* bDefaultValue: the default value
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: item boolean value, return bDefaultValue when the item not exist
|
||||
*/
|
||||
bool iniGetBoolValue(const char *szSectionName, const char *szItemName, \
|
||||
IniContext *pContext, const bool bDefaultValue);
|
||||
bool iniGetBoolValueEx(const char *szSectionName,
|
||||
const char *szItemName, IniContext *pContext,
|
||||
const bool bDefaultValue, const bool bRetryGlobal);
|
||||
|
||||
/** get item double value
|
||||
* parameters:
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* szSectionName: the section name, NULL or empty string for
|
||||
* global section
|
||||
* szItemName: the item name
|
||||
* pContext: the ini context
|
||||
* dbDefaultValue: the default value
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: item value, return dbDefaultValue when the item not exist
|
||||
*/
|
||||
double iniGetDoubleValue(const char *szSectionName, const char *szItemName, \
|
||||
IniContext *pContext, const double dbDefaultValue);
|
||||
double iniGetDoubleValueEx(const char *szSectionName,
|
||||
const char *szItemName, IniContext *pContext,
|
||||
const double dbDefaultValue, const bool bRetryGlobal);
|
||||
|
||||
/** get item correct double value
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* dbDefaultValue: the default value
|
||||
* dbMinValue: the min value to check (including)
|
||||
* dbMaxValue: the max value to check (including)
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: double value, return dbDefaultValue when the item not exist
|
||||
*/
|
||||
double iniGetDoubleCorrectValueEx(IniFullContext *pIniContext,
|
||||
const char *szItemName, const double dbDefaultValue,
|
||||
const double dbMinValue, const double dbMaxValue,
|
||||
const bool bRetryGlobal);
|
||||
|
||||
/** get item percent double value
|
||||
* parameters:
|
||||
* ini_ctx: the full context
|
||||
* item_name: the item name
|
||||
* item_value: store the item value
|
||||
* default_value: the default value
|
||||
* retry_global: if fetch from global section when the item not exist
|
||||
* return: error no, 0 for success, != 0 for fail
|
||||
*/
|
||||
int iniGetPercentValueEx(IniFullContext *ini_ctx,
|
||||
const char *item_name, double *item_value,
|
||||
const double default_value, const bool retry_global);
|
||||
|
||||
/** get item correct double value (show as percentage)
|
||||
* parameters:
|
||||
* pIniContext: the full ini context
|
||||
* szItemName: the item name
|
||||
* dbItemValue: store the item value
|
||||
* dbDefaultValue: the default value
|
||||
* dbMinValue: the min value to check (including)
|
||||
* dbMaxValue: the max value to check (including)
|
||||
* bRetryGlobal: if fetch from global section when the item not exist
|
||||
* return: error no, 0 for success, != 0 for fail
|
||||
*/
|
||||
int iniGetPercentCorrectValueEx(IniFullContext *pIniContext,
|
||||
const char *szItemName, double *dbItemValue,
|
||||
const double dbDefaultValue, const double dbMinValue,
|
||||
const double dbMaxValue, const bool bRetryGlobal);
|
||||
|
||||
/** print all items
|
||||
* parameters:
|
||||
|
|
@ -244,7 +513,7 @@ static inline const char *iniGetConfigPath(IniContext *pContext)
|
|||
return pContext->config_path;
|
||||
}
|
||||
|
||||
/** return the items of global section
|
||||
/** return the section names
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
* sections: the section array
|
||||
|
|
@ -255,6 +524,60 @@ static inline const char *iniGetConfigPath(IniContext *pContext)
|
|||
int iniGetSectionNames(IniContext *pContext, IniSectionInfo *sections,
|
||||
const int max_size, int *nCount);
|
||||
|
||||
/** return the section names
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
* prefix: the prefix of section name
|
||||
* sections: the section array
|
||||
* max_size: the max size of sections
|
||||
* nCount: return the section count
|
||||
* return: errno, 0 for success, != 0 for fail
|
||||
*/
|
||||
int iniGetSectionNamesByPrefix(IniContext *pContext, const char *szPrefix,
|
||||
IniSectionInfo *sections, const int max_size, int *nCount);
|
||||
|
||||
/** return the section names
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
* filter_func: the section name filter function
|
||||
* args: the extra data pointer
|
||||
* sections: the section array
|
||||
* max_size: the max size of sections
|
||||
* nCount: return the section count
|
||||
* return: errno, 0 for success, != 0 for fail
|
||||
*/
|
||||
int iniGetSectionNamesEx(IniContext *pContext, IniSectionNameFilterFunc
|
||||
filter_func, void *args, IniSectionInfo *sections,
|
||||
const int max_size, int *nCount);
|
||||
|
||||
/** get matched section count
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
* filter_func: the section name filter function
|
||||
* args: the extra data pointer
|
||||
* return: matched section count
|
||||
*/
|
||||
int iniGetSectionCountEx(IniContext *pContext, IniSectionNameFilterFunc
|
||||
filter_func, void *args);
|
||||
|
||||
/** get matched section count
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
* prefix: the prefix of section name
|
||||
* return: matched section count
|
||||
*/
|
||||
int iniGetSectionCountByPrefix(IniContext *pContext, const char *szPrefix);
|
||||
|
||||
/** get section count
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
* return: section count
|
||||
*/
|
||||
static inline int iniGetSectionCount(IniContext *pContext)
|
||||
{
|
||||
return pContext->sections.item_count;
|
||||
}
|
||||
|
||||
/** return the items of global section
|
||||
* parameters:
|
||||
* pContext: the ini context
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com
|
||||
*
|
||||
* FastLib may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastLib source kit.
|
||||
* Please visit the FastLib Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,3 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com
|
||||
*
|
||||
* FastLib may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastLib source kit.
|
||||
* Please visit the FastLib Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
|
||||
#ifndef IO_OPT_H_
|
||||
#define IO_OPT_H_
|
||||
|
||||
|
|
|
|||
243
src/ioevent.c
243
src/ioevent.c
|
|
@ -1,8 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "fc_memory.h"
|
||||
#include "ioevent.h"
|
||||
|
||||
#if IOEVENT_USE_KQUEUE
|
||||
|
|
@ -29,71 +45,119 @@ int kqueue_ev_convert(int16_t event, uint16_t flags)
|
|||
}
|
||||
#endif
|
||||
|
||||
int ioevent_init(IOEventPoller *ioevent, const int size,
|
||||
const int timeout_ms, const int extra_events)
|
||||
int ioevent_init(IOEventPoller *ioevent, const char *service_name,
|
||||
const bool use_io_uring, const int size, const int timeout_ms,
|
||||
const int extra_events)
|
||||
{
|
||||
int bytes;
|
||||
int bytes;
|
||||
|
||||
ioevent->size = size;
|
||||
ioevent->extra_events = extra_events;
|
||||
ioevent->iterator.index = 0;
|
||||
ioevent->iterator.count = 0;
|
||||
ioevent->iterator.index = 0;
|
||||
ioevent->iterator.count = 0;
|
||||
ioevent->service_name = service_name;
|
||||
ioevent->size = size;
|
||||
ioevent->extra_events = extra_events;
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
ioevent->poll_fd = epoll_create(ioevent->size);
|
||||
if (ioevent->poll_fd < 0) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
bytes = sizeof(struct epoll_event) * size;
|
||||
ioevent->events = (struct epoll_event *)malloc(bytes);
|
||||
#ifdef OS_LINUX
|
||||
#if IOEVENT_USE_URING
|
||||
ioevent->use_io_uring = use_io_uring;
|
||||
if (use_io_uring) {
|
||||
int result;
|
||||
if ((result=io_uring_queue_init(size, &ioevent->ring, 0)) < 0) {
|
||||
return -result;
|
||||
}
|
||||
ioevent->cqe = NULL;
|
||||
ioevent->submit_count = 0;
|
||||
ioevent->send_zc_logged = false;
|
||||
ioevent->send_zc_done_notify = false;
|
||||
} else {
|
||||
#endif
|
||||
ioevent->poll_fd = epoll_create(ioevent->size);
|
||||
if (ioevent->poll_fd < 0) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
bytes = sizeof(struct epoll_event) * size;
|
||||
ioevent->events = (struct epoll_event *)fc_malloc(bytes);
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
ioevent->poll_fd = kqueue();
|
||||
if (ioevent->poll_fd < 0) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
bytes = sizeof(struct kevent) * size;
|
||||
ioevent->events = (struct kevent *)malloc(bytes);
|
||||
ioevent->poll_fd = kqueue();
|
||||
if (ioevent->poll_fd < 0) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
bytes = sizeof(struct kevent) * size;
|
||||
ioevent->events = (struct kevent *)fc_malloc(bytes);
|
||||
#elif IOEVENT_USE_PORT
|
||||
ioevent->poll_fd = port_create();
|
||||
if (ioevent->poll_fd < 0) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
bytes = sizeof(port_event_t) * size;
|
||||
ioevent->events = (port_event_t *)malloc(bytes);
|
||||
ioevent->poll_fd = port_create();
|
||||
if (ioevent->poll_fd < 0) {
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
}
|
||||
bytes = sizeof(port_event_t) * size;
|
||||
ioevent->events = (port_event_t *)fc_malloc(bytes);
|
||||
#endif
|
||||
|
||||
if (ioevent->events == NULL) {
|
||||
close(ioevent->poll_fd);
|
||||
ioevent->poll_fd = -1;
|
||||
return ENOMEM;
|
||||
}
|
||||
ioevent_set_timeout(ioevent, timeout_ms);
|
||||
#if IOEVENT_USE_URING
|
||||
if (!ioevent->use_io_uring) {
|
||||
#endif
|
||||
if (ioevent->events == NULL) {
|
||||
close(ioevent->poll_fd);
|
||||
ioevent->poll_fd = -1;
|
||||
return ENOMEM;
|
||||
}
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
ioevent_set_timeout(ioevent, timeout_ms);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ioevent_destroy(IOEventPoller *ioevent)
|
||||
{
|
||||
if (ioevent->events != NULL) {
|
||||
free(ioevent->events);
|
||||
ioevent->events = NULL;
|
||||
}
|
||||
#if IOEVENT_USE_URING
|
||||
if (ioevent->use_io_uring) {
|
||||
io_uring_queue_exit(&ioevent->ring);
|
||||
} else {
|
||||
#endif
|
||||
if (ioevent->events != NULL) {
|
||||
free(ioevent->events);
|
||||
ioevent->events = NULL;
|
||||
}
|
||||
|
||||
if (ioevent->poll_fd >= 0) {
|
||||
close(ioevent->poll_fd);
|
||||
ioevent->poll_fd = -1;
|
||||
}
|
||||
if (ioevent->poll_fd >= 0) {
|
||||
close(ioevent->poll_fd);
|
||||
ioevent->poll_fd = -1;
|
||||
}
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e,
|
||||
void *data)
|
||||
int ioevent_attach(IOEventPoller *ioevent, const int fd,
|
||||
const int e, void *data)
|
||||
{
|
||||
#if IOEVENT_USE_EPOLL
|
||||
struct epoll_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = e | ioevent->extra_events;
|
||||
ev.data.ptr = data;
|
||||
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||
#ifdef OS_LINUX
|
||||
#if IOEVENT_USE_URING
|
||||
if (ioevent->use_io_uring) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&ioevent->ring);
|
||||
if (sqe == NULL) {
|
||||
return ENOSPC;
|
||||
}
|
||||
io_uring_prep_poll_multishot(sqe, fd, e | ioevent->extra_events);
|
||||
sqe->user_data = (long)data;
|
||||
ioevent->submit_count++;
|
||||
return 0;
|
||||
} else {
|
||||
#endif
|
||||
struct epoll_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = e | ioevent->extra_events;
|
||||
ev.data.ptr = data;
|
||||
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_ADD, fd, &ev);
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
struct kevent ev[2];
|
||||
int n = 0;
|
||||
|
|
@ -112,15 +176,32 @@ int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e,
|
|||
#endif
|
||||
}
|
||||
|
||||
int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e,
|
||||
void *data)
|
||||
int ioevent_modify(IOEventPoller *ioevent, const int fd,
|
||||
const int e, void *data)
|
||||
{
|
||||
#if IOEVENT_USE_EPOLL
|
||||
struct epoll_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = e | ioevent->extra_events;
|
||||
ev.data.ptr = data;
|
||||
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_MOD, fd, &ev);
|
||||
#ifdef OS_LINUX
|
||||
#if IOEVENT_USE_URING
|
||||
if (ioevent->use_io_uring) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&ioevent->ring);
|
||||
if (sqe == NULL) {
|
||||
return ENOSPC;
|
||||
}
|
||||
io_uring_prep_poll_update(sqe, sqe->user_data, sqe->user_data,
|
||||
e | ioevent->extra_events, IORING_POLL_UPDATE_EVENTS);
|
||||
sqe->user_data = (long)data;
|
||||
ioevent->submit_count++;
|
||||
return 0;
|
||||
} else {
|
||||
#endif
|
||||
struct epoll_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.events = e | ioevent->extra_events;
|
||||
ev.data.ptr = data;
|
||||
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_MOD, fd, &ev);
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
struct kevent ev[2];
|
||||
int result;
|
||||
|
|
@ -155,8 +236,25 @@ int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e,
|
|||
|
||||
int ioevent_detach(IOEventPoller *ioevent, const int fd)
|
||||
{
|
||||
#if IOEVENT_USE_EPOLL
|
||||
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||
#ifdef OS_LINUX
|
||||
#if IOEVENT_USE_URING
|
||||
if (ioevent->use_io_uring) {
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&ioevent->ring);
|
||||
if (sqe == NULL) {
|
||||
return ENOSPC;
|
||||
}
|
||||
io_uring_prep_cancel_fd(sqe, fd, 0);
|
||||
/* set sqe->flags MUST after io_uring_prep_xxx */
|
||||
sqe->flags = IOSQE_CQE_SKIP_SUCCESS;
|
||||
ioevent->submit_count++;
|
||||
return 0;
|
||||
} else {
|
||||
#endif
|
||||
return epoll_ctl(ioevent->poll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
struct kevent ev[1];
|
||||
int r, w;
|
||||
|
|
@ -175,15 +273,33 @@ int ioevent_detach(IOEventPoller *ioevent, const int fd)
|
|||
|
||||
int ioevent_poll(IOEventPoller *ioevent)
|
||||
{
|
||||
#if IOEVENT_USE_EPOLL
|
||||
return epoll_wait(ioevent->poll_fd, ioevent->events, ioevent->size, ioevent->timeout);
|
||||
#ifdef OS_LINUX
|
||||
#if IOEVENT_USE_URING
|
||||
if (ioevent->use_io_uring) {
|
||||
int result;
|
||||
result = io_uring_wait_cqe_timeout(&ioevent->ring,
|
||||
&ioevent->cqe, &ioevent->timeout);
|
||||
if (result < 0) {
|
||||
errno = -result;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
#endif
|
||||
return epoll_wait(ioevent->poll_fd, ioevent->events,
|
||||
ioevent->size, ioevent->timeout_ms);
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
return kevent(ioevent->poll_fd, NULL, 0, ioevent->events, ioevent->size, &ioevent->timeout);
|
||||
return kevent(ioevent->poll_fd, NULL, 0, ioevent->events,
|
||||
ioevent->size, &ioevent->timeout);
|
||||
#elif IOEVENT_USE_PORT
|
||||
int result;
|
||||
int retval;
|
||||
unsigned int nget = 1;
|
||||
if((retval = port_getn(ioevent->poll_fd, ioevent->events,
|
||||
if((retval=port_getn(ioevent->poll_fd, ioevent->events,
|
||||
ioevent->size, &nget, &ioevent->timeout)) == 0)
|
||||
{
|
||||
result = (int)nget;
|
||||
|
|
@ -209,4 +325,3 @@ int ioevent_poll(IOEventPoller *ioevent)
|
|||
#error port me
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
201
src/ioevent.h
201
src/ioevent.h
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __IOEVENT_H__
|
||||
#define __IOEVENT_H__
|
||||
|
||||
|
|
@ -5,17 +20,28 @@
|
|||
#include <poll.h>
|
||||
#include <sys/time.h>
|
||||
#include "_os_define.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define IOEVENT_TIMEOUT 0x8000
|
||||
#define IOEVENT_TIMEOUT (1 << 20)
|
||||
#define IOEVENT_NOTIFY (1 << 21) //for io_uring send_zc done callback
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
#ifdef OS_LINUX
|
||||
#include <sys/epoll.h>
|
||||
#define IOEVENT_EDGE_TRIGGER EPOLLET
|
||||
#endif
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
#define IOEVENT_READ EPOLLIN
|
||||
#define IOEVENT_WRITE EPOLLOUT
|
||||
#define IOEVENT_ERROR (EPOLLERR | EPOLLPRI | EPOLLHUP)
|
||||
|
||||
#elif IOEVENT_USE_URING
|
||||
#include <sys/mount.h>
|
||||
#include <liburing.h>
|
||||
#define IOEVENT_READ POLLIN
|
||||
#define IOEVENT_WRITE POLLOUT
|
||||
#define IOEVENT_ERROR (POLLERR | POLLPRI | POLLHUP)
|
||||
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
#include <sys/event.h>
|
||||
#include <sys/poll.h>
|
||||
|
|
@ -50,18 +76,33 @@ int kqueue_ev_convert(int16_t event, uint16_t flags);
|
|||
#endif
|
||||
|
||||
typedef struct ioevent_puller {
|
||||
const char *service_name;
|
||||
int size; //max events (fd)
|
||||
int extra_events;
|
||||
int poll_fd;
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
struct io_uring ring;
|
||||
int submit_count;
|
||||
bool send_zc_logged;
|
||||
bool send_zc_done_notify; //if callback when send_zc done
|
||||
bool use_io_uring;
|
||||
#endif
|
||||
|
||||
int poll_fd;
|
||||
struct {
|
||||
int index;
|
||||
int count;
|
||||
} iterator; //for deal event loop
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
#ifdef OS_LINUX
|
||||
struct epoll_event *events;
|
||||
int timeout;
|
||||
int timeout_ms; //for epoll
|
||||
#if IOEVENT_USE_URING
|
||||
struct io_uring_cqe *cqe;
|
||||
struct __kernel_timespec timeout;
|
||||
#endif
|
||||
bool zero_timeout;
|
||||
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
struct kevent *events;
|
||||
struct timespec timeout;
|
||||
|
|
@ -69,9 +110,10 @@ typedef struct ioevent_puller {
|
|||
port_event_t *events;
|
||||
timespec_t timeout;
|
||||
#endif
|
||||
|
||||
} IOEventPoller;
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
#if OS_LINUX
|
||||
#define IOEVENT_GET_EVENTS(ioevent, index) \
|
||||
(ioevent)->events[index].events
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
|
|
@ -84,7 +126,7 @@ typedef struct ioevent_puller {
|
|||
#error port me
|
||||
#endif
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
#ifdef OS_LINUX
|
||||
#define IOEVENT_GET_DATA(ioevent, index) \
|
||||
(ioevent)->events[index].data.ptr
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
|
|
@ -97,7 +139,7 @@ typedef struct ioevent_puller {
|
|||
#error port me
|
||||
#endif
|
||||
|
||||
#if IOEVENT_USE_EPOLL
|
||||
#ifdef OS_LINUX
|
||||
#define IOEVENT_CLEAR_DATA(ioevent, index) \
|
||||
(ioevent)->events[index].data.ptr = NULL
|
||||
#elif IOEVENT_USE_KQUEUE
|
||||
|
|
@ -114,24 +156,39 @@ typedef struct ioevent_puller {
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ioevent_init(IOEventPoller *ioevent, const int size,
|
||||
const int timeout_ms, const int extra_events);
|
||||
int ioevent_init(IOEventPoller *ioevent, const char *service_name,
|
||||
const bool use_io_uring, const int size, const int timeout_ms,
|
||||
const int extra_events);
|
||||
void ioevent_destroy(IOEventPoller *ioevent);
|
||||
|
||||
int ioevent_attach(IOEventPoller *ioevent, const int fd, const int e,
|
||||
void *data);
|
||||
int ioevent_modify(IOEventPoller *ioevent, const int fd, const int e,
|
||||
void *data);
|
||||
int ioevent_attach(IOEventPoller *ioevent, const int fd,
|
||||
const int e, void *data);
|
||||
int ioevent_modify(IOEventPoller *ioevent, const int fd,
|
||||
const int e, void *data);
|
||||
int ioevent_detach(IOEventPoller *ioevent, const int fd);
|
||||
int ioevent_poll(IOEventPoller *ioevent);
|
||||
|
||||
static inline void ioevent_set_timeout(IOEventPoller *ioevent, const int timeout_ms)
|
||||
static inline void ioevent_set_timeout(IOEventPoller *ioevent,
|
||||
const int timeout_ms)
|
||||
{
|
||||
#if IOEVENT_USE_EPOLL
|
||||
ioevent->timeout = timeout_ms;
|
||||
ioevent->timeout_ms = timeout_ms;
|
||||
#else
|
||||
ioevent->timeout.tv_sec = timeout_ms / 1000;
|
||||
ioevent->timeout.tv_nsec = 1000000 * (timeout_ms % 1000);
|
||||
#if IOEVENT_USE_URING
|
||||
if (!ioevent->use_io_uring) {
|
||||
ioevent->timeout_ms = timeout_ms;
|
||||
} else {
|
||||
#endif
|
||||
ioevent->timeout.tv_sec = timeout_ms / 1000;
|
||||
ioevent->timeout.tv_nsec = 1000000 * (timeout_ms % 1000);
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef OS_LINUX
|
||||
ioevent->zero_timeout = (timeout_ms == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -141,6 +198,114 @@ static inline int ioevent_poll_ex(IOEventPoller *ioevent, const int timeout_ms)
|
|||
return ioevent_poll(ioevent);
|
||||
}
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
static inline void ioevent_set_send_zc_done_notify(
|
||||
IOEventPoller *ioevent, const bool need_notify)
|
||||
{
|
||||
ioevent->send_zc_done_notify = need_notify;
|
||||
}
|
||||
|
||||
static inline int ioevent_uring_submit(IOEventPoller *ioevent)
|
||||
{
|
||||
int result;
|
||||
|
||||
ioevent->submit_count = 0;
|
||||
while (1) {
|
||||
result = io_uring_submit(&ioevent->ring);
|
||||
if (result < 0) {
|
||||
if (result != -EINTR) {
|
||||
return -result;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline struct io_uring_sqe *ioevent_uring_get_sqe(IOEventPoller *ioevent)
|
||||
{
|
||||
struct io_uring_sqe *sqe = io_uring_get_sqe(&ioevent->ring);
|
||||
if (sqe == NULL) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"io_uring_get_sqe fail", __LINE__);
|
||||
}
|
||||
return sqe;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_recv(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, int sockfd,
|
||||
void *buf, size_t size, void *user_data)
|
||||
{
|
||||
io_uring_prep_recv(sqe, sockfd, buf, size, 0);
|
||||
sqe->user_data = (long)user_data;
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_send(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, int sockfd,
|
||||
void *buf, size_t len, void *user_data)
|
||||
{
|
||||
io_uring_prep_send(sqe, sockfd, buf, len, 0);
|
||||
sqe->user_data = (long)user_data;
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_writev(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, int sockfd, const struct iovec *iovecs,
|
||||
unsigned nr_vecs, void *user_data)
|
||||
{
|
||||
io_uring_prep_writev(sqe, sockfd, iovecs, nr_vecs, 0);
|
||||
sqe->user_data = (long)user_data;
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_send_zc(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, int sockfd,
|
||||
void *buf, size_t len, void *user_data)
|
||||
{
|
||||
io_uring_prep_send_zc(sqe, sockfd, buf, len, 0,
|
||||
#ifdef IORING_SEND_ZC_REPORT_USAGE
|
||||
IORING_SEND_ZC_REPORT_USAGE
|
||||
#else
|
||||
0
|
||||
#endif
|
||||
);
|
||||
sqe->user_data = (long)user_data;
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_close(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, int fd, void *user_data)
|
||||
{
|
||||
io_uring_prep_close(sqe, fd);
|
||||
if (user_data == NULL) {
|
||||
/* set sqe->flags MUST after io_uring_prep_xxx */
|
||||
sqe->flags = IOSQE_CQE_SKIP_SUCCESS;
|
||||
} else {
|
||||
sqe->user_data = (long)user_data;
|
||||
}
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_cancel(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, void *user_data)
|
||||
{
|
||||
io_uring_prep_cancel(sqe, user_data, 0);
|
||||
sqe->user_data = (long)user_data;
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
static inline void ioevent_uring_prep_connect(IOEventPoller *ioevent,
|
||||
struct io_uring_sqe *sqe, int fd, const struct sockaddr *addr,
|
||||
socklen_t addrlen, void *user_data)
|
||||
{
|
||||
io_uring_prep_connect(sqe, fd, addr, addrlen);
|
||||
sqe->user_data = (long)user_data;
|
||||
ioevent->submit_count++;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,57 +1,133 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "sched_thread.h"
|
||||
#include "logger.h"
|
||||
#include "ioevent_loop.h"
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
static int ioevent_process_by_uring(IOEventPoller *ioevent)
|
||||
{
|
||||
int result;
|
||||
unsigned head;
|
||||
unsigned count = 0;
|
||||
IOEventEntry *pEntry;
|
||||
|
||||
result = io_uring_wait_cqe_timeout(&ioevent->ring,
|
||||
&ioevent->cqe, &ioevent->timeout);
|
||||
switch (result) {
|
||||
case 0:
|
||||
break;
|
||||
case -ETIME:
|
||||
case -EINTR:
|
||||
return 0;
|
||||
default:
|
||||
result *= -1;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"io_uring_wait_cqe fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
io_uring_for_each_cqe(&ioevent->ring, head, ioevent->cqe) {
|
||||
count++;
|
||||
pEntry = (IOEventEntry *)ioevent->cqe->user_data;
|
||||
if (pEntry != NULL) {
|
||||
if (ioevent->cqe->flags & IORING_CQE_F_NOTIF) {
|
||||
if (ioevent->send_zc_done_notify) {
|
||||
pEntry->callback(pEntry->fd, IOEVENT_NOTIFY, pEntry);
|
||||
}
|
||||
|
||||
#ifdef IORING_NOTIF_USAGE_ZC_COPIED
|
||||
if (!ioevent->send_zc_logged) {
|
||||
struct fast_task_info *task;
|
||||
|
||||
task = (struct fast_task_info *)pEntry;
|
||||
ioevent->send_zc_logged = true;
|
||||
if (ioevent->cqe->res & IORING_NOTIF_USAGE_ZC_COPIED) {
|
||||
logWarning("file: "__FILE__", line: %d, %s "
|
||||
"client %s:%u, io_uring send_zc: memory "
|
||||
"copy instead of zero copy!", __LINE__,
|
||||
ioevent->service_name, task->client_ip,
|
||||
task->port);
|
||||
} else {
|
||||
logInfo("file: "__FILE__", line: %d, %s "
|
||||
"client %s:%u, io_uring send_zc: zero "
|
||||
"copy OK.", __LINE__, ioevent->service_name,
|
||||
task->client_ip, task->port);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
pEntry->res = ioevent->cqe->res;
|
||||
pEntry->callback(pEntry->fd, 0, pEntry);
|
||||
}
|
||||
} else {
|
||||
logWarning("file: "__FILE__", line: %d, "
|
||||
"io_uring unexpected flags: %d, result: %d", __LINE__,
|
||||
ioevent->cqe->flags, ioevent->cqe->res);
|
||||
}
|
||||
}
|
||||
|
||||
io_uring_cq_advance(&ioevent->ring, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void deal_ioevents(IOEventPoller *ioevent)
|
||||
{
|
||||
int event;
|
||||
IOEventEntry *pEntry;
|
||||
|
||||
for (ioevent->iterator.index=0; ioevent->iterator.index < ioevent->iterator.
|
||||
count; ioevent->iterator.index++)
|
||||
for (ioevent->iterator.index=0; ioevent->iterator.index < ioevent->
|
||||
iterator.count; ioevent->iterator.index++)
|
||||
{
|
||||
event = IOEVENT_GET_EVENTS(ioevent, ioevent->iterator.index);
|
||||
pEntry = (IOEventEntry *)IOEVENT_GET_DATA(ioevent,
|
||||
ioevent->iterator.index);
|
||||
if (pEntry != NULL) {
|
||||
pEntry->callback(pEntry->fd, event, pEntry->timer.data);
|
||||
pEntry->callback(pEntry->fd, event, pEntry);
|
||||
}
|
||||
else {
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"ignore iovent : %d, index: %d", __LINE__, event, ioevent->iterator.index);
|
||||
"ignore ioevent : %d, index: %d",
|
||||
__LINE__, event, ioevent->iterator.index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ioevent_remove(IOEventPoller *ioevent, void *data)
|
||||
static int ioevent_process_by_poll(IOEventPoller *ioevent)
|
||||
{
|
||||
IOEventEntry *pEntry;
|
||||
int index;
|
||||
int result;
|
||||
|
||||
if (ioevent->iterator.index >= ioevent->iterator.count)
|
||||
{
|
||||
return ENOENT;
|
||||
ioevent->iterator.count = ioevent_poll(ioevent);
|
||||
if (ioevent->iterator.count > 0) {
|
||||
deal_ioevents(ioevent);
|
||||
}
|
||||
|
||||
pEntry = (IOEventEntry *)IOEVENT_GET_DATA(ioevent,
|
||||
ioevent->iterator.index);
|
||||
if (pEntry != NULL && pEntry->timer.data == data) {
|
||||
return 0; //do NOT clear current entry
|
||||
}
|
||||
|
||||
for (index=ioevent->iterator.index + 1; index < ioevent->iterator.count;
|
||||
index++)
|
||||
{
|
||||
pEntry = (IOEventEntry *)IOEVENT_GET_DATA(ioevent, index);
|
||||
if (pEntry != NULL && pEntry->timer.data == data) {
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"clear iovent data: %p", __LINE__, data);
|
||||
IOEVENT_CLEAR_DATA(ioevent, index);
|
||||
return 0;
|
||||
else if (ioevent->iterator.count < 0) {
|
||||
result = errno != 0 ? errno : EINVAL;
|
||||
if (result != EINTR) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"ioevent_poll fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return ENOENT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void deal_timeouts(FastTimerEntry *head)
|
||||
|
|
@ -66,138 +142,214 @@ static void deal_timeouts(FastTimerEntry *head)
|
|||
current = entry;
|
||||
entry = entry->next;
|
||||
|
||||
current->prev = current->next = NULL; //must set NULL because NOT in time wheel
|
||||
pEventEntry = (IOEventEntry *)current->data;
|
||||
if (pEventEntry != NULL)
|
||||
{
|
||||
pEventEntry->callback(pEventEntry->fd, IOEVENT_TIMEOUT,
|
||||
current->data);
|
||||
}
|
||||
/* must set NULL because NOT in time wheel */
|
||||
current->prev = current->next = NULL;
|
||||
pEventEntry = (IOEventEntry *)current;
|
||||
pEventEntry->callback(pEventEntry->fd, IOEVENT_TIMEOUT, current);
|
||||
}
|
||||
}
|
||||
|
||||
void iovent_add_to_deleted_list(struct fast_task_info *pTask)
|
||||
{
|
||||
if (pTask->thread_data == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
pTask->next = pTask->thread_data->deleted_list;
|
||||
pTask->thread_data->deleted_list = pTask;
|
||||
}
|
||||
|
||||
int ioevent_loop(struct nio_thread_data *pThreadData,
|
||||
int ioevent_loop(struct nio_thread_data *thread_data,
|
||||
IOEventCallback recv_notify_callback, TaskCleanUpCallback
|
||||
clean_up_callback, volatile bool *continue_flag)
|
||||
{
|
||||
int result;
|
||||
IOEventEntry ev_notify;
|
||||
struct ioevent_notify_entry ev_notify;
|
||||
FastTimerEntry head;
|
||||
struct fast_task_info *pTask;
|
||||
struct fast_task_info *task;
|
||||
time_t last_check_time;
|
||||
int save_extra_events;
|
||||
int count;
|
||||
#ifdef OS_LINUX
|
||||
uint32_t sched_counter;
|
||||
#endif
|
||||
bool sched_pull;
|
||||
|
||||
memset(&ev_notify, 0, sizeof(ev_notify));
|
||||
ev_notify.fd = pThreadData->pipe_fds[0];
|
||||
ev_notify.callback = recv_notify_callback;
|
||||
if (ioevent_attach(&pThreadData->ev_puller,
|
||||
pThreadData->pipe_fds[0], IOEVENT_READ,
|
||||
&ev_notify) != 0)
|
||||
ev_notify.event.fd = FC_NOTIFY_READ_FD(thread_data);
|
||||
ev_notify.event.callback = recv_notify_callback;
|
||||
ev_notify.thread_data = thread_data;
|
||||
|
||||
save_extra_events = thread_data->ev_puller.extra_events;
|
||||
thread_data->ev_puller.extra_events = 0; //disable edge trigger temporarily
|
||||
if (ioevent_attach(&thread_data->ev_puller, ev_notify.
|
||||
event.fd, IOEVENT_READ, &ev_notify) != 0)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logCrit("file: "__FILE__", line: %d, " \
|
||||
"ioevent_attach fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
logCrit("file: "__FILE__", line: %d, "
|
||||
"ioevent_attach fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
thread_data->ev_puller.extra_events = save_extra_events; //restore
|
||||
|
||||
pThreadData->deleted_list = NULL;
|
||||
#ifdef OS_LINUX
|
||||
sched_counter = 0;
|
||||
#endif
|
||||
|
||||
thread_data->deleted_list = NULL;
|
||||
last_check_time = g_current_time;
|
||||
while (*continue_flag)
|
||||
{
|
||||
pThreadData->ev_puller.iterator.count = ioevent_poll(&pThreadData->ev_puller);
|
||||
if (pThreadData->ev_puller.iterator.count > 0)
|
||||
{
|
||||
deal_ioevents(&pThreadData->ev_puller);
|
||||
}
|
||||
else if (pThreadData->ev_puller.iterator.count < 0)
|
||||
{
|
||||
result = errno != 0 ? errno : EINVAL;
|
||||
if (result != EINTR)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"ioevent_poll fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
while (*continue_flag) {
|
||||
#ifdef OS_LINUX
|
||||
if (thread_data->ev_puller.zero_timeout) {
|
||||
sched_pull = (sched_counter++ & 8) != 0;
|
||||
} else {
|
||||
sched_pull = true;
|
||||
}
|
||||
#else
|
||||
sched_pull = true;
|
||||
#endif
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
if (thread_data->ev_puller.use_io_uring) {
|
||||
if (thread_data->ev_puller.submit_count > 0) {
|
||||
if ((result=ioevent_uring_submit(&thread_data->
|
||||
ev_puller)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"io_uring_submit fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sched_pull) {
|
||||
#if IOEVENT_USE_URING
|
||||
if (thread_data->ev_puller.use_io_uring) {
|
||||
if ((result=ioevent_process_by_uring(&thread_data->
|
||||
ev_puller)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
if ((result=ioevent_process_by_poll(&thread_data->
|
||||
ev_puller)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (thread_data->busy_polling_callback != NULL) {
|
||||
thread_data->busy_polling_callback(thread_data);
|
||||
}
|
||||
|
||||
if (thread_data->deleted_list != NULL) {
|
||||
//count = 0;
|
||||
while (thread_data->deleted_list != NULL) {
|
||||
task = thread_data->deleted_list;
|
||||
thread_data->deleted_list = task->next;
|
||||
|
||||
if (task->polling.in_queue) {
|
||||
fc_list_del_init(&task->polling.dlink);
|
||||
task->polling.in_queue = false;
|
||||
if (fc_list_empty(&task->thread_data->polling_queue)) {
|
||||
ioevent_set_timeout(&task->thread_data->ev_puller,
|
||||
task->thread_data->timeout_ms);
|
||||
}
|
||||
}
|
||||
clean_up_callback(task);
|
||||
//count++;
|
||||
}
|
||||
//logInfo("cleanup task count: %d", count);
|
||||
}
|
||||
|
||||
if (pThreadData->deleted_list != NULL)
|
||||
{
|
||||
count = 0;
|
||||
while (pThreadData->deleted_list != NULL)
|
||||
{
|
||||
pTask = pThreadData->deleted_list;
|
||||
pThreadData->deleted_list = pTask->next;
|
||||
|
||||
clean_up_callback(pTask);
|
||||
count++;
|
||||
}
|
||||
logDebug("cleanup task count: %d", count);
|
||||
}
|
||||
|
||||
if (g_current_time - last_check_time > 0)
|
||||
{
|
||||
if (g_current_time - last_check_time > 0) {
|
||||
last_check_time = g_current_time;
|
||||
count = fast_timer_timeouts_get(
|
||||
&pThreadData->timer, g_current_time, &head);
|
||||
&thread_data->timer, g_current_time, &head);
|
||||
if (count > 0)
|
||||
{
|
||||
deal_timeouts(&head);
|
||||
}
|
||||
}
|
||||
|
||||
if (pThreadData->thread_loop_callback != NULL) {
|
||||
pThreadData->thread_loop_callback(pThreadData);
|
||||
if (thread_data->notify.enabled) {
|
||||
int64_t n;
|
||||
if ((n=__sync_fetch_and_add(&thread_data->notify.counter, 0)) != 0)
|
||||
{
|
||||
__sync_fetch_and_sub(&thread_data->notify.counter, n);
|
||||
/*
|
||||
logInfo("file: "__FILE__", line: %d, "
|
||||
"n ==== %"PRId64", now: %"PRId64,
|
||||
__LINE__, n, __sync_fetch_and_add(
|
||||
&thread_data->notify.counter, 0));
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
if (thread_data->thread_loop_callback != NULL) {
|
||||
thread_data->thread_loop_callback(thread_data);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ioevent_set(struct fast_task_info *pTask, struct nio_thread_data *pThread,
|
||||
int sock, short event, IOEventCallback callback, const int timeout)
|
||||
int ioevent_set(struct fast_task_info *task, struct nio_thread_data *pThread,
|
||||
int sock, short event, IOEventCallback callback,
|
||||
const int timeout)
|
||||
{
|
||||
int result;
|
||||
|
||||
pTask->thread_data = pThread;
|
||||
pTask->event.fd = sock;
|
||||
pTask->event.callback = callback;
|
||||
if (ioevent_attach(&pThread->ev_puller,
|
||||
sock, event, pTask) < 0)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOENT;
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"ioevent_attach fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
pTask->event.timer.data = pTask;
|
||||
pTask->event.timer.expires = g_current_time + timeout;
|
||||
result = fast_timer_add(&pThread->timer, &pTask->event.timer);
|
||||
if (result != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"fast_timer_add fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
task->thread_data = pThread;
|
||||
task->event.fd = sock;
|
||||
task->event.callback = callback;
|
||||
#if IOEVENT_USE_URING
|
||||
if (pThread->ev_puller.use_io_uring) {
|
||||
if (FC_URING_OP_TYPE(task) == IORING_OP_NOP) {
|
||||
if ((result=uring_prep_first_recv(task)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"uring_prep_recv fail, fd: %d, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, sock, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
logWarning("file: "__FILE__", line: %d, "
|
||||
"skip uring_prep_recv, fd: %d, port: %d, "
|
||||
"in progress op type: %d, timeout: %"PRId64,
|
||||
__LINE__, sock, task->port, FC_URING_OP_TYPE(task),
|
||||
task->event.timer.expires);
|
||||
*/
|
||||
}
|
||||
} else {
|
||||
#endif
|
||||
if (ioevent_attach(&pThread->ev_puller, sock, event, task) < 0) {
|
||||
result = errno != 0 ? errno : ENOENT;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"ioevent_attach fail, fd: %d, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, sock, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
#if IOEVENT_USE_URING
|
||||
}
|
||||
#endif
|
||||
|
||||
task->event.timer.expires = g_current_time + timeout;
|
||||
fast_timer_add(&pThread->timer, &task->event.timer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ioevent_reset(struct fast_task_info *task, int new_fd, short event)
|
||||
{
|
||||
if (task->event.fd == new_fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (task->event.fd >= 0)
|
||||
{
|
||||
ioevent_detach(&task->thread_data->ev_puller, task->event.fd);
|
||||
}
|
||||
|
||||
task->event.fd = new_fd;
|
||||
return ioevent_attach(&task->thread_data->ev_puller, new_fd, event, task);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _IOEVENT_LOOP_H
|
||||
#define _IOEVENT_LOOP_H
|
||||
|
||||
#include "fast_task_queue.h"
|
||||
#if IOEVENT_USE_URING
|
||||
#include "sockopt.h"
|
||||
#endif
|
||||
|
||||
#define fc_hold_task_ex(task, inc_count) __sync_add_and_fetch( \
|
||||
&task->reffer_count, inc_count)
|
||||
|
||||
#define fc_hold_task(task) fc_hold_task_ex(task, 1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int ioevent_loop(struct nio_thread_data *pThreadData,
|
||||
int ioevent_loop(struct nio_thread_data *thread_data,
|
||||
IOEventCallback recv_notify_callback, TaskCleanUpCallback
|
||||
clean_up_callback, volatile bool *continue_flag);
|
||||
|
||||
//remove entry from ready list
|
||||
int ioevent_remove(IOEventPoller *ioevent, void *data);
|
||||
int ioevent_set(struct fast_task_info *task, struct nio_thread_data *pThread,
|
||||
int sock, short event, IOEventCallback callback,
|
||||
const int timeout);
|
||||
|
||||
int ioevent_set(struct fast_task_info *pTask, struct nio_thread_data *pThread,
|
||||
int sock, short event, IOEventCallback callback, const int timeout);
|
||||
int ioevent_reset(struct fast_task_info *task, int new_fd, short event);
|
||||
|
||||
void iovent_add_to_deleted_list(struct fast_task_info *pTask);
|
||||
static inline bool ioevent_is_canceled(struct fast_task_info *task)
|
||||
{
|
||||
return __sync_fetch_and_add(&task->canceled, 0) != 0;
|
||||
}
|
||||
|
||||
//only called by the nio thread
|
||||
static inline void ioevent_add_to_deleted_list(struct fast_task_info *task)
|
||||
{
|
||||
if (!__sync_bool_compare_and_swap(&task->canceled, 0, 1))
|
||||
{
|
||||
logWarning("file: "__FILE__", line: %d, "
|
||||
"task %p already canceled", __LINE__, task);
|
||||
return;
|
||||
}
|
||||
|
||||
task->next = task->thread_data->deleted_list;
|
||||
task->thread_data->deleted_list = task;
|
||||
}
|
||||
|
||||
static inline int ioevent_notify_thread(struct nio_thread_data *thread_data)
|
||||
{
|
||||
int64_t n;
|
||||
int result;
|
||||
|
||||
if (__sync_fetch_and_add(&thread_data->notify.counter, 1) == 0)
|
||||
{
|
||||
n = 1;
|
||||
if (write(FC_NOTIFY_WRITE_FD(thread_data), &n, sizeof(n)) != sizeof(n))
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"write to fd %d fail, errno: %d, error info: %s",
|
||||
__LINE__, FC_NOTIFY_WRITE_FD(thread_data),
|
||||
result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IOEVENT_USE_URING
|
||||
|
||||
#define SET_OP_TYPE_AND_HOLD_TASK(task, _op_type) \
|
||||
struct io_uring_sqe *sqe; \
|
||||
if ((sqe=ioevent_uring_get_sqe(&task->thread_data->ev_puller)) == NULL) { \
|
||||
return ENOSPC; \
|
||||
} \
|
||||
FC_URING_OP_TYPE(task) = _op_type; \
|
||||
fc_hold_task(task)
|
||||
|
||||
static inline int uring_prep_recv_data(struct fast_task_info *task,
|
||||
char *buff, const int len)
|
||||
{
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_RECV);
|
||||
ioevent_uring_prep_recv(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, buff, len, task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_first_recv(struct fast_task_info *task)
|
||||
{
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_RECV);
|
||||
ioevent_uring_prep_recv(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->recv.ptr->data,
|
||||
task->recv.ptr->size, task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_next_recv(struct fast_task_info *task)
|
||||
{
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_RECV);
|
||||
ioevent_uring_prep_recv(&task->thread_data->ev_puller, sqe,
|
||||
task->event.fd, task->recv.ptr->data + task->recv.ptr->offset,
|
||||
task->recv.ptr->length - task->recv.ptr->offset, task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_first_send(struct fast_task_info *task)
|
||||
{
|
||||
if (task->iovec_array.iovs != NULL) {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_WRITEV);
|
||||
ioevent_uring_prep_writev(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->iovec_array.iovs,
|
||||
FC_MIN(task->iovec_array.count, IOV_MAX),
|
||||
task);
|
||||
} else {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_SEND);
|
||||
ioevent_uring_prep_send(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->send.ptr->data,
|
||||
task->send.ptr->length, task);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_next_send(struct fast_task_info *task)
|
||||
{
|
||||
if (task->iovec_array.iovs != NULL) {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_WRITEV);
|
||||
ioevent_uring_prep_writev(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->iovec_array.iovs,
|
||||
FC_MIN(task->iovec_array.count, IOV_MAX),
|
||||
task);
|
||||
} else {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_SEND);
|
||||
ioevent_uring_prep_send(&task->thread_data->ev_puller, sqe,
|
||||
task->event.fd, task->send.ptr->data + task->send.ptr->offset,
|
||||
task->send.ptr->length - task->send.ptr->offset, task);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_first_send_zc(struct fast_task_info *task)
|
||||
{
|
||||
if (task->iovec_array.iovs != NULL) {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_WRITEV);
|
||||
ioevent_uring_prep_writev(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->iovec_array.iovs,
|
||||
FC_MIN(task->iovec_array.count, IOV_MAX),
|
||||
task);
|
||||
} else if (task->send.ptr->length < 4096) {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_SEND);
|
||||
ioevent_uring_prep_send(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->send.ptr->data,
|
||||
task->send.ptr->length, task);
|
||||
} else {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_SEND_ZC);
|
||||
ioevent_uring_prep_send_zc(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->send.ptr->data,
|
||||
task->send.ptr->length, task);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_next_send_zc(struct fast_task_info *task)
|
||||
{
|
||||
if (task->iovec_array.iovs != NULL) {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_WRITEV);
|
||||
ioevent_uring_prep_writev(&task->thread_data->ev_puller,
|
||||
sqe, task->event.fd, task->iovec_array.iovs,
|
||||
FC_MIN(task->iovec_array.count, IOV_MAX),
|
||||
task);
|
||||
} else if (task->send.ptr->length - task->send.ptr->offset < 4096) {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_SEND);
|
||||
ioevent_uring_prep_send(&task->thread_data->ev_puller, sqe,
|
||||
task->event.fd, task->send.ptr->data + task->send.ptr->offset,
|
||||
task->send.ptr->length - task->send.ptr->offset, task);
|
||||
} else {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_SEND_ZC);
|
||||
ioevent_uring_prep_send_zc(&task->thread_data->ev_puller, sqe,
|
||||
task->event.fd, task->send.ptr->data + task->send.ptr->offset,
|
||||
task->send.ptr->length - task->send.ptr->offset, task);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_close_fd(struct fast_task_info *task)
|
||||
{
|
||||
struct io_uring_sqe *sqe;
|
||||
|
||||
if ((sqe=ioevent_uring_get_sqe(&task->thread_data->ev_puller)) == NULL) {
|
||||
return ENOSPC;
|
||||
}
|
||||
|
||||
/* do NOT need callback */
|
||||
ioevent_uring_prep_close(&task->thread_data->
|
||||
ev_puller, sqe, task->event.fd, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_cancel(struct fast_task_info *task)
|
||||
{
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_ASYNC_CANCEL);
|
||||
ioevent_uring_prep_cancel(&task->thread_data->ev_puller, sqe, task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int uring_prep_connect(struct fast_task_info *task)
|
||||
{
|
||||
int result;
|
||||
sockaddr_convert_t *convert;
|
||||
|
||||
if ((task->event.fd=socketCreateEx2(AF_UNSPEC, task->server_ip,
|
||||
O_NONBLOCK, NULL, &result)) < 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
convert = (sockaddr_convert_t *)(task->send.ptr->data +
|
||||
task->send.ptr->size - 2 * sizeof(sockaddr_convert_t));
|
||||
if ((result=setsockaddrbyip(task->server_ip, task->port, convert)) != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
do {
|
||||
SET_OP_TYPE_AND_HOLD_TASK(task, IORING_OP_CONNECT);
|
||||
ioevent_uring_prep_connect(&task->thread_data->ev_puller, sqe,
|
||||
task->event.fd, &convert->sa.addr, convert->len, task);
|
||||
} while (0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "shared_func.h"
|
||||
#include "fc_memory.h"
|
||||
#include "json_parser.h"
|
||||
|
||||
#define EXPECT_STR_LEN 80
|
||||
|
|
@ -13,7 +29,7 @@
|
|||
(ch >= '0' && ch <= '9') || (ch == '_' || ch == '-' || \
|
||||
ch == '.'))
|
||||
|
||||
int detect_json_type(const string_t *input)
|
||||
int fc_detect_json_type(const string_t *input)
|
||||
{
|
||||
if (input->len < 2) {
|
||||
return FC_JSON_TYPE_STRING;
|
||||
|
|
@ -29,18 +45,9 @@ int detect_json_type(const string_t *input)
|
|||
return FC_JSON_TYPE_STRING;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *str; //input string
|
||||
const char *p; //current
|
||||
const char *end;
|
||||
string_t element;
|
||||
char *error_info;
|
||||
int error_size;
|
||||
} ParseContext;
|
||||
|
||||
static void set_parse_error(const char *str, const char *current,
|
||||
const int expect_len, const char *front,
|
||||
char *error_info, const int error_size)
|
||||
string_t *error_info, const int error_size)
|
||||
{
|
||||
const char *show_str;
|
||||
int show_len;
|
||||
|
|
@ -50,26 +57,18 @@ static void set_parse_error(const char *str, const char *current,
|
|||
show_len = expect_len;
|
||||
}
|
||||
show_str = current - show_len;
|
||||
snprintf(error_info, error_size, "%s, input: %.*s",
|
||||
front, show_len, show_str);
|
||||
error_info->len = snprintf(error_info->str, error_size,
|
||||
"%s, input: %.*s", front, show_len, show_str);
|
||||
}
|
||||
|
||||
static int json_escape_string(const string_t *input, string_t *output,
|
||||
char *error_info, const int error_size)
|
||||
static int json_escape_string(fc_json_context_t *context,
|
||||
const string_t *input, char *output)
|
||||
{
|
||||
const char *src;
|
||||
const char *end;
|
||||
char *dest;
|
||||
int size;
|
||||
|
||||
size = 2 * input->len + 1;
|
||||
output->str = (char *)malloc(size);
|
||||
if (output->str == NULL) {
|
||||
snprintf(error_info, error_size, "malloc %d bytes fail", size);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
dest = output->str;
|
||||
dest = output;
|
||||
end = input->str + input->len;
|
||||
for (src=input->str; src<end; src++) {
|
||||
switch (*src) {
|
||||
|
|
@ -93,13 +92,21 @@ static int json_escape_string(const string_t *input, string_t *output,
|
|||
*dest++ = '\\';
|
||||
*dest++ = 'b';
|
||||
break;
|
||||
case '\f':
|
||||
*dest++ = '\\';
|
||||
*dest++ = 'f';
|
||||
break;
|
||||
case '\"':
|
||||
*dest++ = '\\';
|
||||
*dest++ = '\"';
|
||||
break;
|
||||
case '\'':
|
||||
case '\0':
|
||||
*dest++ = '\\';
|
||||
*dest++ = '\'';
|
||||
*dest++ = 'u';
|
||||
*dest++ = '0';
|
||||
*dest++ = '0';
|
||||
*dest++ = '0';
|
||||
*dest++ = '0';
|
||||
break;
|
||||
default:
|
||||
*dest++ = *src;
|
||||
|
|
@ -107,18 +114,19 @@ static int json_escape_string(const string_t *input, string_t *output,
|
|||
}
|
||||
}
|
||||
|
||||
*dest = '\0';
|
||||
output->len = dest - output->str;
|
||||
return 0;
|
||||
return dest - output;
|
||||
}
|
||||
|
||||
static int next_json_element(ParseContext *context)
|
||||
static int next_json_element(fc_json_context_t *context)
|
||||
{
|
||||
char *dest;
|
||||
const char *start;
|
||||
char buff[128];
|
||||
char quote_ch;
|
||||
int unicode;
|
||||
int i;
|
||||
|
||||
dest = context->element.str;
|
||||
dest = context->decode.element.str;
|
||||
quote_ch = *context->p;
|
||||
if (quote_ch == '\"' || quote_ch == '\'') {
|
||||
context->p++;
|
||||
|
|
@ -127,9 +135,42 @@ static int next_json_element(ParseContext *context)
|
|||
if (++context->p == context->end) {
|
||||
set_parse_error(context->str, context->p,
|
||||
EXPECT_STR_LEN, "expect a character after \\",
|
||||
context->error_info, context->error_size);
|
||||
&context->error_info, context->error_size);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (*context->p == 'u') { //unicode
|
||||
start = ++context->p; //skip charator 'u'
|
||||
i = 0;
|
||||
while (i < 4 && context->p < context->end &&
|
||||
IS_HEX_CHAR(*context->p))
|
||||
{
|
||||
buff[i++] = *context->p;
|
||||
++context->p;
|
||||
}
|
||||
if (i != 4) {
|
||||
set_parse_error(context->str, start,
|
||||
EXPECT_STR_LEN, "expect 4 hex characters "
|
||||
"after \\u", &context->error_info,
|
||||
context->error_size);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
buff[i] = '\0';
|
||||
unicode = strtol(buff, NULL, 16);
|
||||
if (unicode < 0x80) {
|
||||
*dest++ = unicode;
|
||||
} else if (unicode < 0x800) {
|
||||
*dest++ = 0xC0 | ((unicode >> 6) & 0x1F);
|
||||
*dest++ = 0x80 | (unicode & 0x3F);
|
||||
} else {
|
||||
*dest++ = 0xE0 | ((unicode >> 12) & 0x0F);
|
||||
*dest++ = 0x80 | ((unicode >> 6) & 0x3F);
|
||||
*dest++ = 0x80 | (unicode & 0x3F);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (*context->p) {
|
||||
case '\\':
|
||||
*dest++ = '\\';
|
||||
|
|
@ -146,20 +187,21 @@ static int next_json_element(ParseContext *context)
|
|||
case 'n':
|
||||
*dest++ = '\n';
|
||||
break;
|
||||
case 'b':
|
||||
case 'f':
|
||||
*dest++ = '\f';
|
||||
break;
|
||||
case 'b':
|
||||
*dest++ = '\b';
|
||||
break;
|
||||
case '"':
|
||||
*dest++ = '\"';
|
||||
break;
|
||||
case '\'':
|
||||
*dest++ = '\'';
|
||||
break;
|
||||
default:
|
||||
sprintf(buff, "invalid escaped character: %c(0x%x)",
|
||||
*context->p, (unsigned char)*context->p);
|
||||
set_parse_error(context->str, context->p + 1, EXPECT_STR_LEN,
|
||||
buff, context->error_info, context->error_size);
|
||||
set_parse_error(context->str, context->p + 1,
|
||||
EXPECT_STR_LEN, buff, &context->error_info,
|
||||
context->error_size);
|
||||
return EINVAL;
|
||||
}
|
||||
context->p++;
|
||||
|
|
@ -171,7 +213,7 @@ static int next_json_element(ParseContext *context)
|
|||
if (context->p == context->end) {
|
||||
sprintf(buff, "expect closed character: %c", quote_ch);
|
||||
set_parse_error(context->str, context->p, EXPECT_STR_LEN,
|
||||
buff, context->error_info, context->error_size);
|
||||
buff, &context->error_info, context->error_size);
|
||||
return EINVAL;
|
||||
}
|
||||
context->p++; //skip quote char
|
||||
|
|
@ -182,12 +224,12 @@ static int next_json_element(ParseContext *context)
|
|||
}
|
||||
|
||||
*dest = '\0';
|
||||
context->element.len = dest - context->element.str;
|
||||
context->decode.element.len = dest - context->decode.element.str;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_alloc_array(common_array_t *array,
|
||||
char *error_info, const int error_size)
|
||||
static int check_alloc_array(fc_json_context_t *context,
|
||||
fc_common_array_t *array)
|
||||
{
|
||||
int bytes;
|
||||
if (array->count < array->alloc) {
|
||||
|
|
@ -201,346 +243,322 @@ static int check_alloc_array(common_array_t *array,
|
|||
}
|
||||
|
||||
bytes = array->element_size * array->alloc;
|
||||
array->elements = realloc(array->elements, bytes);
|
||||
array->elements = fc_realloc(array->elements, bytes);
|
||||
if (array->elements == NULL) {
|
||||
snprintf(error_info, error_size, "malloc %d bytes fail", bytes);
|
||||
context->error_info.len = snprintf(context->error_info.str,
|
||||
context->error_size, "realloc %d bytes fail", bytes);
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int check_alloc_json_array(json_array_t *array,
|
||||
char *error_info, const int error_size)
|
||||
static int prepare_json_parse(fc_json_context_t *context,
|
||||
const string_t *input, const char lquote,
|
||||
const char rquote)
|
||||
{
|
||||
return check_alloc_array((common_array_t *)array, error_info, error_size);
|
||||
}
|
||||
|
||||
static inline int check_alloc_json_map(json_map_t *array,
|
||||
char *error_info, const int error_size)
|
||||
{
|
||||
return check_alloc_array((common_array_t *)array, error_info, error_size);
|
||||
}
|
||||
|
||||
static int prepare_json_parse(const string_t *input, common_array_t *array,
|
||||
char *error_info, const int error_size,
|
||||
const char lquote, const char rquote, ParseContext *context)
|
||||
{
|
||||
int buff_len;
|
||||
|
||||
array->elements = NULL;
|
||||
array->count = array->alloc = 0;
|
||||
array->buff = NULL;
|
||||
int expect_size;
|
||||
int result;
|
||||
|
||||
if (input->len < 2) {
|
||||
snprintf(error_info, error_size, "json string is too short");
|
||||
context->error_info.len = snprintf(context->error_info.str,
|
||||
context->error_size, "json string is too short");
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (input->str[0] != lquote) {
|
||||
snprintf(error_info, error_size,
|
||||
"json array must start with \"%c\"", lquote);
|
||||
context->error_info.len = snprintf(context->error_info.str, context->
|
||||
error_size, "json array must start with \"%c\"", lquote);
|
||||
return EINVAL;
|
||||
}
|
||||
if (input->str[input->len - 1] != rquote) {
|
||||
snprintf(error_info, error_size,
|
||||
"json array must end with \"%c\"", rquote);
|
||||
context->error_info.len = snprintf(context->error_info.str, context->
|
||||
error_size, "json array must end with \"%c\"", rquote);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
buff_len = input->len - 2;
|
||||
array->buff = (char *)malloc(buff_len + 1);
|
||||
if (array->buff == NULL) {
|
||||
snprintf(error_info, error_size,
|
||||
"malloc %d bytes fail", buff_len + 1);
|
||||
return ENOMEM;
|
||||
expect_size = input->len;
|
||||
if (context->output.alloc_size < expect_size) {
|
||||
if ((result=fc_realloc_buffer(&context->output, context->
|
||||
init_buff_size, expect_size)) != 0)
|
||||
{
|
||||
context->error_info.len = snprintf(context->error_info.str,
|
||||
context->error_size, "realloc buffer fail");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
context->error_info = error_info;
|
||||
context->error_size = error_size;
|
||||
context->element.str = array->buff;
|
||||
context->element.len = 0;
|
||||
context->decode.element.str = context->output.buff;
|
||||
context->decode.element.len = 0;
|
||||
context->str = input->str;
|
||||
context->p = input->str + 1;
|
||||
context->end = input->str + input->len - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decode_json_array(const string_t *input, json_array_t *array,
|
||||
char *error_info, const int error_size)
|
||||
static inline void json_quote_string(fc_json_context_t
|
||||
*context, const string_t *input, char **buff)
|
||||
{
|
||||
ParseContext context;
|
||||
int result;
|
||||
|
||||
array->element_size = sizeof(string_t);
|
||||
if ((result=prepare_json_parse(input, (common_array_t *)array,
|
||||
error_info, error_size, '[', ']', &context)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
while (context.p < context.end) {
|
||||
while (context.p < context.end && JSON_SPACE(*context.p)) {
|
||||
context.p++;
|
||||
}
|
||||
|
||||
if (context.p == context.end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*context.p == ',') {
|
||||
set_parse_error(input->str, context.p + 1,
|
||||
EXPECT_STR_LEN, "unexpect comma \",\"",
|
||||
error_info, error_size);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((result=next_json_element(&context)) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (context.p < context.end && JSON_SPACE(*context.p)) {
|
||||
context.p++;
|
||||
}
|
||||
if (context.p < context.end) {
|
||||
if (*context.p == ',') {
|
||||
context.p++; //skip comma
|
||||
} else {
|
||||
set_parse_error(input->str, context.p,
|
||||
EXPECT_STR_LEN, "expect comma \",\"",
|
||||
error_info, error_size);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((result=check_alloc_json_array(array, error_info, error_size)) != 0) {
|
||||
array->count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
array->elements[array->count++] = context.element;
|
||||
context.element.str += context.element.len + 1;
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
free_json_array(array);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void free_common_array(common_array_t *array)
|
||||
{
|
||||
if (array->elements != NULL) {
|
||||
free(array->elements);
|
||||
array->elements = NULL;
|
||||
array->count = 0;
|
||||
}
|
||||
|
||||
if (array->buff != NULL) {
|
||||
free(array->buff);
|
||||
array->buff = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int json_quote_string(const string_t *input, char **buff,
|
||||
char *error_info, const int error_size)
|
||||
{
|
||||
int result;
|
||||
string_t escaped;
|
||||
char *p;
|
||||
|
||||
if ((result=json_escape_string(input, &escaped,
|
||||
error_info, error_size)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
p = *buff;
|
||||
*p++ = '"';
|
||||
memcpy(p, escaped.str, escaped.len);
|
||||
p += escaped.len;
|
||||
p += json_escape_string(context, input, p);
|
||||
*p++ = '"';
|
||||
|
||||
*buff = p;
|
||||
free(escaped.str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int encode_json_array(json_array_t *array, string_t *output,
|
||||
char *error_info, const int error_size)
|
||||
int fc_encode_json_array_ex(fc_json_context_t *context,
|
||||
const string_t *elements, const int count,
|
||||
BufferInfo *buffer)
|
||||
{
|
||||
string_t *el;
|
||||
string_t *end;
|
||||
const string_t *el;
|
||||
const string_t *end;
|
||||
char *p;
|
||||
int result;
|
||||
int size;
|
||||
int expect_size;
|
||||
|
||||
end = array->elements + array->count;
|
||||
size = 3;
|
||||
for (el=array->elements; el<end; el++) {
|
||||
size += 2 * el->len + 3;
|
||||
expect_size = 3;
|
||||
end = elements + count;
|
||||
for (el=elements; el<end; el++) {
|
||||
expect_size += 6 * el->len + 3;
|
||||
}
|
||||
|
||||
output->str = (char *)malloc(size);
|
||||
if (output->str == NULL) {
|
||||
snprintf(error_info, error_size, "malloc %d bytes fail", size);
|
||||
return ENOMEM;
|
||||
if (buffer->alloc_size < expect_size) {
|
||||
if ((context->error_no=fc_realloc_buffer(buffer, context->
|
||||
init_buff_size, expect_size)) != 0)
|
||||
{
|
||||
context->error_info.len = snprintf(context->error_info.str,
|
||||
context->error_size, "realloc buffer fail");
|
||||
return context->error_no;
|
||||
}
|
||||
}
|
||||
|
||||
p = output->str;
|
||||
p = buffer->buff;
|
||||
*p++ = '[';
|
||||
for (el=array->elements; el<end; el++) {
|
||||
if (el > array->elements) {
|
||||
for (el=elements; el<end; el++) {
|
||||
if (el > elements) {
|
||||
*p++ = ',';
|
||||
}
|
||||
|
||||
if ((result=json_quote_string(el, &p, error_info, error_size)) != 0) {
|
||||
free_json_string(output);
|
||||
return result;
|
||||
}
|
||||
json_quote_string(context, el, &p);
|
||||
}
|
||||
|
||||
*p++ = ']';
|
||||
*p = '\0';
|
||||
output->len = p - output->str;
|
||||
buffer->length = p - buffer->buff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int decode_json_map(const string_t *input, json_map_t *map,
|
||||
char *error_info, const int error_size)
|
||||
int fc_encode_json_map_ex(fc_json_context_t *context,
|
||||
const key_value_pair_t *elements, const int count,
|
||||
BufferInfo *buffer)
|
||||
{
|
||||
ParseContext context;
|
||||
key_value_pair_t kv_pair;
|
||||
int result;
|
||||
|
||||
map->element_size = sizeof(key_value_pair_t);
|
||||
if ((result=prepare_json_parse(input, (common_array_t *)map,
|
||||
error_info, error_size, '{', '}', &context)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
result = 0;
|
||||
while (context.p < context.end) {
|
||||
while (context.p < context.end && JSON_SPACE(*context.p)) {
|
||||
context.p++;
|
||||
}
|
||||
|
||||
if (context.p == context.end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*context.p == ',') {
|
||||
set_parse_error(input->str, context.p + 1,
|
||||
EXPECT_STR_LEN, "unexpect comma \",\"",
|
||||
error_info, error_size);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((result=next_json_element(&context)) != 0) {
|
||||
break;
|
||||
}
|
||||
while (context.p < context.end && JSON_SPACE(*context.p)) {
|
||||
context.p++;
|
||||
}
|
||||
if (!(context.p < context.end && *context.p == ':')) {
|
||||
set_parse_error(input->str, context.p,
|
||||
EXPECT_STR_LEN, "expect colon \":\"",
|
||||
error_info, error_size);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
context.p++; //skip colon
|
||||
|
||||
kv_pair.key = context.element;
|
||||
context.element.str += context.element.len + 1;
|
||||
|
||||
while (context.p < context.end && JSON_SPACE(*context.p)) {
|
||||
context.p++;
|
||||
}
|
||||
if ((result=next_json_element(&context)) != 0) {
|
||||
break;
|
||||
}
|
||||
while (context.p < context.end && JSON_SPACE(*context.p)) {
|
||||
context.p++;
|
||||
}
|
||||
if (context.p < context.end) {
|
||||
if (*context.p == ',') {
|
||||
context.p++; //skip comma
|
||||
} else {
|
||||
set_parse_error(input->str, context.p,
|
||||
EXPECT_STR_LEN, "expect comma \",\"",
|
||||
error_info, error_size);
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kv_pair.value = context.element;
|
||||
context.element.str += context.element.len + 1;
|
||||
|
||||
if ((result=check_alloc_json_map(map, error_info, error_size)) != 0) {
|
||||
map->count = 0;
|
||||
break;
|
||||
}
|
||||
map->elements[map->count++] = kv_pair;
|
||||
}
|
||||
|
||||
if (result != 0) {
|
||||
free_json_map(map);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int encode_json_map(json_map_t *map, string_t *output,
|
||||
char *error_info, const int error_size)
|
||||
{
|
||||
key_value_pair_t *pair;
|
||||
key_value_pair_t *end;
|
||||
const key_value_pair_t *pair;
|
||||
const key_value_pair_t *end;
|
||||
char *p;
|
||||
int result;
|
||||
int size;
|
||||
int expect_size;
|
||||
|
||||
end = map->elements + map->count;
|
||||
size = 3;
|
||||
for (pair=map->elements; pair<end; pair++) {
|
||||
size += 2 * (pair->key.len + pair->value.len + 2) + 1;
|
||||
expect_size = 3;
|
||||
end = elements + count;
|
||||
for (pair=elements; pair<end; pair++) {
|
||||
expect_size += 6 * (pair->key.len + pair->value.len) + 5;
|
||||
}
|
||||
|
||||
output->str = (char *)malloc(size);
|
||||
if (output->str == NULL) {
|
||||
snprintf(error_info, error_size, "malloc %d bytes fail", size);
|
||||
return ENOMEM;
|
||||
if (buffer->alloc_size < expect_size) {
|
||||
if ((context->error_no=fc_realloc_buffer(buffer, context->
|
||||
init_buff_size, expect_size)) != 0)
|
||||
{
|
||||
context->error_info.len = snprintf(context->error_info.str,
|
||||
context->error_size, "realloc buffer fail");
|
||||
return context->error_no;
|
||||
}
|
||||
}
|
||||
|
||||
p = output->str;
|
||||
p = buffer->buff;
|
||||
*p++ = '{';
|
||||
for (pair=map->elements; pair<end; pair++) {
|
||||
if (pair > map->elements) {
|
||||
for (pair=elements; pair<end; pair++) {
|
||||
if (pair > elements) {
|
||||
*p++ = ',';
|
||||
}
|
||||
|
||||
if ((result=json_quote_string(&pair->key, &p,
|
||||
error_info, error_size)) != 0)
|
||||
{
|
||||
free_json_string(output);
|
||||
return result;
|
||||
}
|
||||
json_quote_string(context, &pair->key, &p);
|
||||
*p++ = ':';
|
||||
if ((result=json_quote_string(&pair->value, &p,
|
||||
error_info, error_size)) != 0)
|
||||
{
|
||||
free_json_string(output);
|
||||
return result;
|
||||
}
|
||||
json_quote_string(context, &pair->value, &p);
|
||||
}
|
||||
|
||||
*p++ = '}';
|
||||
*p = '\0';
|
||||
output->len = p - output->str;
|
||||
buffer->length = p - buffer->buff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define JSON_DECODE_COPY_STRING(ctx, input, dest, src) \
|
||||
do { \
|
||||
if ((context->error_no=fast_mpool_alloc_string_ex2( \
|
||||
&ctx->decode.mpool, dest, src)) != 0) \
|
||||
{ \
|
||||
set_parse_error(input->str, ctx->p, EXPECT_STR_LEN, \
|
||||
"out of memory", &ctx->error_info, ctx->error_size); \
|
||||
return NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
const fc_json_array_t *fc_decode_json_array(fc_json_context_t
|
||||
*context, const string_t *input)
|
||||
{
|
||||
if ((context->error_no=prepare_json_parse(context,
|
||||
input, '[', ']')) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context->jarray.count = 0;
|
||||
while (context->p < context->end) {
|
||||
while (context->p < context->end && JSON_SPACE(*context->p)) {
|
||||
context->p++;
|
||||
}
|
||||
|
||||
if (context->p == context->end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*context->p == ',') {
|
||||
set_parse_error(input->str, context->p + 1,
|
||||
EXPECT_STR_LEN, "unexpect comma \",\"",
|
||||
&context->error_info, context->error_size);
|
||||
context->error_no = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((context->error_no=next_json_element(context)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (context->p < context->end && JSON_SPACE(*context->p)) {
|
||||
context->p++;
|
||||
}
|
||||
if (context->p < context->end) {
|
||||
if (*context->p == ',') {
|
||||
context->p++; //skip comma
|
||||
} else {
|
||||
set_parse_error(input->str, context->p,
|
||||
EXPECT_STR_LEN, "expect comma \",\"",
|
||||
&context->error_info, context->error_size);
|
||||
context->error_no = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ((context->error_no=check_alloc_array(context,
|
||||
(fc_common_array_t *)
|
||||
&context->jarray)) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (context->decode.use_mpool) {
|
||||
JSON_DECODE_COPY_STRING(context, input, context->jarray.elements +
|
||||
context->jarray.count++, &context->decode.element);
|
||||
} else {
|
||||
context->jarray.elements[context->jarray.count++] =
|
||||
context->decode.element;
|
||||
}
|
||||
context->decode.element.str += context->decode.element.len + 1;
|
||||
}
|
||||
|
||||
return &context->jarray;
|
||||
}
|
||||
|
||||
const fc_json_map_t *fc_decode_json_map(fc_json_context_t
|
||||
*context, const string_t *input)
|
||||
{
|
||||
key_value_pair_t kv_pair;
|
||||
|
||||
if ((context->error_no=prepare_json_parse(context,
|
||||
input, '{', '}')) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
context->jmap.count = 0;
|
||||
while (context->p < context->end) {
|
||||
while (context->p < context->end && JSON_SPACE(*context->p)) {
|
||||
context->p++;
|
||||
}
|
||||
|
||||
if (context->p == context->end) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (*context->p == ',') {
|
||||
set_parse_error(input->str, context->p + 1,
|
||||
EXPECT_STR_LEN, "unexpect comma \",\"",
|
||||
&context->error_info, context->error_size);
|
||||
context->error_no = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((context->error_no=next_json_element(context)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
while (context->p < context->end && JSON_SPACE(*context->p)) {
|
||||
context->p++;
|
||||
}
|
||||
if (!(context->p < context->end && *context->p == ':')) {
|
||||
set_parse_error(input->str, context->p,
|
||||
EXPECT_STR_LEN, "expect colon \":\"",
|
||||
&context->error_info, context->error_size);
|
||||
context->error_no = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
context->p++; //skip colon
|
||||
|
||||
kv_pair.key = context->decode.element;
|
||||
context->decode.element.str += context->decode.element.len + 1;
|
||||
|
||||
while (context->p < context->end && JSON_SPACE(*context->p)) {
|
||||
context->p++;
|
||||
}
|
||||
if ((context->error_no=next_json_element(context)) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
while (context->p < context->end && JSON_SPACE(*context->p)) {
|
||||
context->p++;
|
||||
}
|
||||
if (context->p < context->end) {
|
||||
if (*context->p == ',') {
|
||||
context->p++; //skip comma
|
||||
} else {
|
||||
set_parse_error(input->str, context->p,
|
||||
EXPECT_STR_LEN, "expect comma \",\"",
|
||||
&context->error_info, context->error_size);
|
||||
context->error_no = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
kv_pair.value = context->decode.element;
|
||||
context->decode.element.str += context->decode.element.len + 1;
|
||||
|
||||
if ((context->error_no=check_alloc_array(context,
|
||||
(fc_common_array_t *)
|
||||
&context->jmap)) != 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (context->decode.use_mpool) {
|
||||
key_value_pair_t *dest;
|
||||
dest = context->jmap.elements + context->jmap.count++;
|
||||
JSON_DECODE_COPY_STRING(context, input,
|
||||
&dest->key, &kv_pair.key);
|
||||
JSON_DECODE_COPY_STRING(context, input,
|
||||
&dest->value, &kv_pair.value);
|
||||
} else {
|
||||
context->jmap.elements[context->jmap.count++] = kv_pair;
|
||||
}
|
||||
}
|
||||
|
||||
return &context->jmap;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//json_parser.h
|
||||
|
||||
#ifndef _JSON_PARSER_H
|
||||
#define _JSON_PARSER_H
|
||||
#ifndef _FC_JSON_PARSER_H
|
||||
#define _FC_JSON_PARSER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include "common_define.h"
|
||||
#include "shared_func.h"
|
||||
#include "fast_mpool.h"
|
||||
|
||||
#define FC_JSON_TYPE_STRING 1
|
||||
#define FC_JSON_TYPE_ARRAY 2
|
||||
|
|
@ -21,55 +38,200 @@
|
|||
/* for internal use */ \
|
||||
int element_size; \
|
||||
int alloc; \
|
||||
char *buff; \
|
||||
} ARRAY_TYPE
|
||||
|
||||
DEFINE_ARRAY_STRUCT(void, common_array_t);
|
||||
DEFINE_ARRAY_STRUCT(string_t, json_array_t);
|
||||
DEFINE_ARRAY_STRUCT(key_value_pair_t, json_map_t);
|
||||
DEFINE_ARRAY_STRUCT(void, fc_common_array_t);
|
||||
DEFINE_ARRAY_STRUCT(string_t, fc_json_array_t);
|
||||
DEFINE_ARRAY_STRUCT(key_value_pair_t, fc_json_map_t);
|
||||
|
||||
typedef struct {
|
||||
BufferInfo output; //for json encode/decode
|
||||
struct {
|
||||
struct fast_mpool_man mpool;
|
||||
string_t element; //string allocator use output buffer
|
||||
bool use_mpool;
|
||||
} decode;
|
||||
int init_buff_size;
|
||||
int error_no;
|
||||
int error_size;
|
||||
char error_holder[256];
|
||||
string_t error_info;
|
||||
|
||||
fc_json_array_t jarray;
|
||||
fc_json_map_t jmap;
|
||||
|
||||
/* for internal use */
|
||||
const char *str; //input string
|
||||
const char *p; //current
|
||||
const char *end;
|
||||
} fc_json_context_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void free_common_array(common_array_t *array);
|
||||
|
||||
static inline void free_json_array(json_array_t *array)
|
||||
static inline void fc_init_common_array(fc_common_array_t *array,
|
||||
const int element_size)
|
||||
{
|
||||
free_common_array((common_array_t *)array);
|
||||
array->elements = NULL;
|
||||
array->element_size = element_size;
|
||||
array->count = array->alloc = 0;
|
||||
}
|
||||
|
||||
static inline void free_json_map(json_map_t *array)
|
||||
static inline void fc_init_json_array(fc_json_array_t *array)
|
||||
{
|
||||
free_common_array((common_array_t *)array);
|
||||
fc_init_common_array((fc_common_array_t *)array, sizeof(string_t));
|
||||
}
|
||||
|
||||
static inline void free_json_string(string_t *buffer)
|
||||
static inline void fc_init_json_map(fc_json_map_t *array)
|
||||
{
|
||||
if (buffer->str != NULL) {
|
||||
free(buffer->str);
|
||||
buffer->str = NULL;
|
||||
buffer->len = 0;
|
||||
fc_init_common_array((fc_common_array_t *)array,
|
||||
sizeof(key_value_pair_t));
|
||||
}
|
||||
|
||||
static inline void fc_free_common_array(fc_common_array_t *array)
|
||||
{
|
||||
if (array->elements != NULL) {
|
||||
free(array->elements);
|
||||
array->elements = NULL;
|
||||
array->count = array->alloc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int detect_json_type(const string_t *input);
|
||||
static inline void fc_free_json_array(fc_json_array_t *array)
|
||||
{
|
||||
fc_free_common_array((fc_common_array_t *)array);
|
||||
}
|
||||
|
||||
int decode_json_array(const string_t *input, json_array_t *array,
|
||||
char *error_info, const int error_size);
|
||||
static inline void fc_free_json_map(fc_json_map_t *array)
|
||||
{
|
||||
fc_free_common_array((fc_common_array_t *)array);
|
||||
}
|
||||
|
||||
int encode_json_array(json_array_t *array, string_t *output,
|
||||
char *error_info, const int error_size);
|
||||
static inline void fc_set_json_error_buffer(fc_json_context_t *ctx,
|
||||
char *error_info, const int error_size)
|
||||
{
|
||||
if (error_info != NULL && error_size > 0) {
|
||||
ctx->error_info.str = error_info;
|
||||
ctx->error_size = error_size;
|
||||
} else {
|
||||
ctx->error_info.str = ctx->error_holder;
|
||||
ctx->error_size = sizeof(ctx->error_holder);
|
||||
}
|
||||
|
||||
int decode_json_map(const string_t *input, json_map_t *map,
|
||||
char *error_info, const int error_size);
|
||||
ctx->error_info.len = 0;
|
||||
*ctx->error_info.str = '\0';
|
||||
}
|
||||
|
||||
int encode_json_map(json_map_t *map, string_t *output,
|
||||
char *error_info, const int error_size);
|
||||
static inline int fc_init_json_context_ex(fc_json_context_t *ctx,
|
||||
const bool decode_use_mpool, const int alloc_size_once,
|
||||
const int init_buff_size, char *error_info,
|
||||
const int error_size)
|
||||
{
|
||||
const int discard_size = 0;
|
||||
|
||||
ctx->output.buff = NULL;
|
||||
ctx->output.alloc_size = ctx->output.length = 0;
|
||||
FC_SET_STRING_NULL(ctx->decode.element);
|
||||
if (init_buff_size > 0) {
|
||||
ctx->init_buff_size = init_buff_size;
|
||||
} else {
|
||||
ctx->init_buff_size = 1024;
|
||||
}
|
||||
fc_init_json_array(&ctx->jarray);
|
||||
fc_init_json_map(&ctx->jmap);
|
||||
|
||||
ctx->error_no = 0;
|
||||
fc_set_json_error_buffer(ctx, error_info, error_size);
|
||||
ctx->decode.use_mpool = decode_use_mpool;
|
||||
if (decode_use_mpool) {
|
||||
return fast_mpool_init(&ctx->decode.mpool,
|
||||
alloc_size_once, discard_size);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int fc_init_json_context(fc_json_context_t *ctx)
|
||||
{
|
||||
const bool decode_use_mpool = false;
|
||||
const int alloc_size_once = 0;
|
||||
const int init_buff_size = 0;
|
||||
|
||||
return fc_init_json_context_ex(ctx, decode_use_mpool,
|
||||
alloc_size_once, init_buff_size, NULL, 0);
|
||||
}
|
||||
|
||||
static inline void fc_reset_json_context(fc_json_context_t *ctx)
|
||||
{
|
||||
if (ctx->decode.use_mpool) {
|
||||
fast_mpool_reset(&ctx->decode.mpool);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void fc_destroy_json_context(fc_json_context_t *ctx)
|
||||
{
|
||||
fc_free_buffer(&ctx->output);
|
||||
fc_free_json_array(&ctx->jarray);
|
||||
fc_free_json_map(&ctx->jmap);
|
||||
if (ctx->decode.use_mpool) {
|
||||
fast_mpool_destroy(&ctx->decode.mpool);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int fc_json_parser_get_error_no(fc_json_context_t *ctx)
|
||||
{
|
||||
return ctx->error_no;
|
||||
}
|
||||
|
||||
static inline const string_t *fc_json_parser_get_error_info(
|
||||
fc_json_context_t *ctx)
|
||||
{
|
||||
return &ctx->error_info;
|
||||
}
|
||||
|
||||
int fc_detect_json_type(const string_t *input);
|
||||
|
||||
int fc_encode_json_array_ex(fc_json_context_t *context,
|
||||
const string_t *elements, const int count,
|
||||
BufferInfo *buffer);
|
||||
|
||||
int fc_encode_json_map_ex(fc_json_context_t *context,
|
||||
const key_value_pair_t *elements, const int count,
|
||||
BufferInfo *buffer);
|
||||
|
||||
static inline const BufferInfo *fc_encode_json_array(fc_json_context_t
|
||||
*context, const string_t *elements, const int count)
|
||||
{
|
||||
if (fc_encode_json_array_ex(context, elements, count,
|
||||
&context->output) == 0)
|
||||
{
|
||||
return &context->output;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline const BufferInfo *fc_encode_json_map(fc_json_context_t
|
||||
*context, const key_value_pair_t *elements, const int count)
|
||||
{
|
||||
if (fc_encode_json_map_ex(context, elements, count,
|
||||
&context->output) == 0)
|
||||
{
|
||||
return &context->output;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const fc_json_array_t *fc_decode_json_array(fc_json_context_t
|
||||
*context, const string_t *input);
|
||||
|
||||
const fc_json_map_t *fc_decode_json_map(fc_json_context_t
|
||||
*context, const string_t *input);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
|
|
@ -24,7 +31,7 @@ bool is_local_host_ip(const char *client_ip)
|
|||
char *p;
|
||||
char *pEnd;
|
||||
|
||||
pEnd = g_local_host_ip_addrs + \
|
||||
pEnd = g_local_host_ip_addrs +
|
||||
IP_ADDRESS_SIZE * g_local_host_ip_count;
|
||||
for (p=g_local_host_ip_addrs; p<pEnd; p+=IP_ADDRESS_SIZE)
|
||||
{
|
||||
|
|
@ -49,9 +56,8 @@ int insert_into_local_host_ip(const char *client_ip)
|
|||
return -1;
|
||||
}
|
||||
|
||||
strcpy(g_local_host_ip_addrs + \
|
||||
IP_ADDRESS_SIZE * g_local_host_ip_count, \
|
||||
client_ip);
|
||||
strcpy(g_local_host_ip_addrs + IP_ADDRESS_SIZE *
|
||||
g_local_host_ip_count, client_ip);
|
||||
g_local_host_ip_count++;
|
||||
return 1;
|
||||
}
|
||||
|
|
@ -76,7 +82,7 @@ const char *local_host_ip_addrs_to_string(char *buff, const int size)
|
|||
|
||||
void log_local_host_ip_addrs()
|
||||
{
|
||||
char buff[512];
|
||||
char buff[1024];
|
||||
logInfo("%s", local_host_ip_addrs_to_string(buff, sizeof(buff)));
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +95,7 @@ void load_local_host_ip_addrs()
|
|||
char *if_alias_prefixes[STORAGE_MAX_ALIAS_PREFIX_COUNT];
|
||||
int alias_count;
|
||||
|
||||
insert_into_local_host_ip(LOCAL_LOOPBACK_IP);
|
||||
insert_into_local_host_ip(LOCAL_LOOPBACK_IPv4);
|
||||
|
||||
memset(if_alias_prefixes, 0, sizeof(if_alias_prefixes));
|
||||
if (*g_if_alias_prefix == '\0')
|
||||
|
|
@ -153,8 +159,8 @@ const char *get_next_local_ip(const char *previous_ip)
|
|||
pEnd = g_local_host_ip_addrs + \
|
||||
IP_ADDRESS_SIZE * g_local_host_ip_count;
|
||||
for (p=g_local_host_ip_addrs; p<pEnd; p+=IP_ADDRESS_SIZE)
|
||||
{
|
||||
if (strcmp(p, LOCAL_LOOPBACK_IP) != 0)
|
||||
{
|
||||
if (!is_loopback_ip(p))
|
||||
{
|
||||
if (found)
|
||||
{
|
||||
|
|
@ -165,7 +171,7 @@ const char *get_next_local_ip(const char *previous_ip)
|
|||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -180,7 +186,10 @@ const char *get_first_local_ip()
|
|||
}
|
||||
else
|
||||
{
|
||||
return LOCAL_LOOPBACK_IP;
|
||||
/* 注意,当系统存在IPv6回环地址时,为了简化系统的改动,
|
||||
会将IPv6回环地址修改成IPv4回环地址返回
|
||||
*/
|
||||
return LOCAL_LOOPBACK_IPv4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,3 +214,17 @@ const char *get_first_local_private_ip()
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void stat_local_host_ip(int *ipv4_count, int *ipv6_count)
|
||||
{
|
||||
const char *ip_addr;
|
||||
|
||||
*ipv4_count = *ipv6_count = 0;
|
||||
ip_addr = NULL;
|
||||
while ((ip_addr=get_next_local_ip(ip_addr)) != NULL) {
|
||||
if (is_ipv6_addr(ip_addr)) {
|
||||
++(*ipv6_count);
|
||||
} else {
|
||||
++(*ipv4_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//local_ip_func.h
|
||||
|
||||
|
|
@ -18,22 +25,32 @@
|
|||
#include "common_define.h"
|
||||
|
||||
#define FAST_IF_ALIAS_PREFIX_MAX_SIZE 32
|
||||
#define FAST_MAX_LOCAL_IP_ADDRS 16
|
||||
#define FAST_MAX_LOCAL_IP_ADDRS 32
|
||||
|
||||
#define LOCAL_LOOPBACK_IP "127.0.0.1"
|
||||
#define LOCAL_LOOPBACK_IPv4 "127.0.0.1"
|
||||
#define LOCAL_LOOPBACK_IPv6 "::1"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int g_local_host_ip_count;
|
||||
extern char g_local_host_ip_addrs[FAST_MAX_LOCAL_IP_ADDRS * \
|
||||
extern char g_local_host_ip_addrs[FAST_MAX_LOCAL_IP_ADDRS *
|
||||
IP_ADDRESS_SIZE];
|
||||
extern char g_if_alias_prefix[FAST_IF_ALIAS_PREFIX_MAX_SIZE];
|
||||
|
||||
void load_local_host_ip_addrs();
|
||||
bool is_local_host_ip(const char *client_ip);
|
||||
|
||||
static inline bool is_loopback_ip(const char *ip_addr)
|
||||
{
|
||||
return (strcmp(ip_addr, LOCAL_LOOPBACK_IPv4) == 0 ||
|
||||
strcmp(ip_addr, LOCAL_LOOPBACK_IPv6) == 0 ||
|
||||
strcasecmp(ip_addr, "fe80::1") == 0);
|
||||
}
|
||||
|
||||
void stat_local_host_ip(int *ipv4_count, int *ipv6_count);
|
||||
|
||||
const char *get_first_local_ip();
|
||||
const char *get_next_local_ip(const char *previous_ip);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
#ifndef _LOCKED_LIST_H
|
||||
#define _LOCKED_LIST_H
|
||||
|
||||
#include "fc_list.h"
|
||||
#include "pthread_func.h"
|
||||
|
||||
typedef struct fc_locked_list {
|
||||
struct fc_list_head head;
|
||||
pthread_mutex_t lock;
|
||||
} FCLockedList;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LOCKED_LIST_LOCK(list) PTHREAD_MUTEX_LOCK(&(list)->lock)
|
||||
#define LOCKED_LIST_UNLOCK(list) PTHREAD_MUTEX_UNLOCK(&(list)->lock)
|
||||
|
||||
static inline int locked_list_init(FCLockedList *list)
|
||||
{
|
||||
int result;
|
||||
if ((result=init_pthread_lock(&list->lock)) != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
FC_INIT_LIST_HEAD(&list->head);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void locked_list_destroy(FCLockedList *list)
|
||||
{
|
||||
pthread_mutex_destroy(&list->lock);
|
||||
}
|
||||
|
||||
static inline void locked_list_add(struct fc_list_head *_new,
|
||||
FCLockedList *list)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
fc_list_add(_new, &list->head);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
}
|
||||
|
||||
static inline void locked_list_add_tail(struct fc_list_head *_new,
|
||||
FCLockedList *list)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
fc_list_add_tail(_new, &list->head);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
}
|
||||
|
||||
static inline void locked_list_move(struct fc_list_head *obj,
|
||||
FCLockedList *list)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
fc_list_move(obj, &list->head);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
}
|
||||
|
||||
static inline void locked_list_move_tail(struct fc_list_head *obj,
|
||||
FCLockedList *list)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
fc_list_move_tail(obj, &list->head);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
}
|
||||
|
||||
static inline void locked_list_del(struct fc_list_head *old,
|
||||
FCLockedList *list)
|
||||
{
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
fc_list_del_init(old);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
}
|
||||
|
||||
static inline int locked_list_empty(FCLockedList *list)
|
||||
{
|
||||
int empty;
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
empty = fc_list_empty(&list->head);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
return empty;
|
||||
}
|
||||
|
||||
static inline int locked_list_count(FCLockedList *list)
|
||||
{
|
||||
int count;
|
||||
PTHREAD_MUTEX_LOCK(&list->lock);
|
||||
count = fc_list_count(&list->head);
|
||||
PTHREAD_MUTEX_UNLOCK(&list->lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
#define locked_list_first_entry(list, type, member, var) \
|
||||
PTHREAD_MUTEX_LOCK(&(list)->lock); \
|
||||
var = fc_list_first_entry(&(list)->head, type, member); \
|
||||
PTHREAD_MUTEX_UNLOCK(&(list)->lock)
|
||||
|
||||
#define locked_list_last_entry(list, type, member, var) \
|
||||
PTHREAD_MUTEX_LOCK(&(list)->lock); \
|
||||
var = fc_list_last_entry(&(list)->head, type, member); \
|
||||
PTHREAD_MUTEX_UNLOCK(&(list)->lock)
|
||||
|
||||
#define locked_list_pop(list, type, member, var) \
|
||||
PTHREAD_MUTEX_LOCK(&(list)->lock); \
|
||||
var = fc_list_first_entry(&(list)->head, type, member); \
|
||||
if (var != NULL) { \
|
||||
fc_list_del_init(&var->member); \
|
||||
} \
|
||||
PTHREAD_MUTEX_UNLOCK(&(list)->lock)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "logger.h"
|
||||
#include "fc_memory.h"
|
||||
#include "shared_func.h"
|
||||
#include "pthread_func.h"
|
||||
#include "locked_timer.h"
|
||||
|
||||
static int locked_timer_init_slots(LockedTimer *timer)
|
||||
{
|
||||
int bytes;
|
||||
int result;
|
||||
LockedTimerSlot *slot;
|
||||
LockedTimerSlot *send;
|
||||
pthread_mutex_t *lock;
|
||||
pthread_mutex_t *lend;
|
||||
|
||||
bytes = sizeof(LockedTimerSlot) * timer->slot_count;
|
||||
timer->slots = (LockedTimerSlot *)fc_malloc(bytes);
|
||||
if (timer->slots == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(timer->slots, 0, bytes);
|
||||
|
||||
send = timer->slots + timer->slot_count;
|
||||
for (slot=timer->slots; slot<send; slot++) {
|
||||
if ((result=init_pthread_lock(&slot->lock)) != 0) {
|
||||
return result;
|
||||
}
|
||||
FC_INIT_LIST_HEAD(&slot->head);
|
||||
}
|
||||
|
||||
timer->entry_shares.locks = (pthread_mutex_t *)fc_malloc(
|
||||
sizeof(pthread_mutex_t) * timer->entry_shares.count);
|
||||
if (timer->entry_shares.locks == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
lend = timer->entry_shares.locks + timer->entry_shares.count;
|
||||
for (lock=timer->entry_shares.locks; lock<lend; lock++) {
|
||||
if ((result=init_pthread_lock(lock)) != 0) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int locked_timer_init_ex(LockedTimer *timer, const int slot_count,
|
||||
const int64_t current_time, const int shared_lock_count,
|
||||
const bool set_lock_index)
|
||||
{
|
||||
if (slot_count <= 0 || current_time <= 0) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
timer->slot_count = slot_count;
|
||||
timer->entry_shares.count = shared_lock_count;
|
||||
timer->entry_shares.set_lock_index = set_lock_index;
|
||||
timer->base_time = current_time; //base time for slot 0
|
||||
timer->current_time = current_time;
|
||||
return locked_timer_init_slots(timer);
|
||||
}
|
||||
|
||||
void locked_timer_destroy(LockedTimer *timer)
|
||||
{
|
||||
LockedTimerSlot *slot;
|
||||
LockedTimerSlot *send;
|
||||
pthread_mutex_t *lock;
|
||||
pthread_mutex_t *lend;
|
||||
|
||||
if (timer->slots == NULL) {
|
||||
return;
|
||||
}
|
||||
send = timer->slots + timer->slot_count;
|
||||
for (slot=timer->slots; slot<send; slot++) {
|
||||
pthread_mutex_destroy(&slot->lock);
|
||||
}
|
||||
|
||||
lend = timer->entry_shares.locks + timer->entry_shares.count;
|
||||
for (lock=timer->entry_shares.locks; lock<lend; lock++) {
|
||||
pthread_mutex_destroy(lock);
|
||||
}
|
||||
free(timer->entry_shares.locks);
|
||||
timer->entry_shares.locks = NULL;
|
||||
timer->entry_shares.count = 0;
|
||||
|
||||
free(timer->slots);
|
||||
timer->slots = NULL;
|
||||
}
|
||||
|
||||
#define TIMER_GET_SLOT_INDEX(timer, expires) \
|
||||
(((expires) - timer->base_time) % timer->slot_count)
|
||||
|
||||
#define TIMER_GET_SLOT_POINTER(timer, expires) \
|
||||
(timer->slots + TIMER_GET_SLOT_INDEX(timer, expires))
|
||||
|
||||
#define TIMER_ENTRY_LOCK(timer, lock_index) \
|
||||
PTHREAD_MUTEX_LOCK(timer->entry_shares.locks + lock_index)
|
||||
|
||||
#define TIMER_ENTRY_UNLOCK(timer, lock_index) \
|
||||
PTHREAD_MUTEX_UNLOCK(timer->entry_shares.locks + lock_index)
|
||||
|
||||
#define TIMER_ENTRY_FETCH_LOCK_INDEX(timer, entry) \
|
||||
(timer->entry_shares.set_lock_index ? \
|
||||
__sync_add_and_fetch(&entry->lock_index, 0) : entry->lock_index)
|
||||
|
||||
#define TIMER_ENTRY_FETCH_AND_LOCK(timer, entry) \
|
||||
lock_index = TIMER_ENTRY_FETCH_LOCK_INDEX(timer, entry); \
|
||||
PTHREAD_MUTEX_LOCK(timer->entry_shares.locks + lock_index)
|
||||
|
||||
#define TIMER_SET_ENTRY_STATUS_AND_SINDEX(timer, slot, entry, lock_index) \
|
||||
do { \
|
||||
TIMER_ENTRY_LOCK(timer, lock_index); \
|
||||
entry->status = FAST_TIMER_STATUS_NORMAL; \
|
||||
entry->slot_index = slot - timer->slots; \
|
||||
TIMER_ENTRY_UNLOCK(timer, lock_index); \
|
||||
} while (0)
|
||||
|
||||
static inline void add_entry(LockedTimer *timer, LockedTimerSlot *slot,
|
||||
LockedTimerEntry *entry, const int64_t expires, const int flags)
|
||||
{
|
||||
int lock_index;
|
||||
if ((flags & FAST_TIMER_FLAGS_SET_ENTRY_LOCK) != 0) {
|
||||
if (timer->entry_shares.set_lock_index) {
|
||||
int old_index;
|
||||
/* init the entry on the first call */
|
||||
lock_index = ((unsigned long)entry) % timer->entry_shares.count;
|
||||
old_index = entry->lock_index;
|
||||
while (!__sync_bool_compare_and_swap(&entry->lock_index,
|
||||
old_index, lock_index))
|
||||
{
|
||||
old_index = __sync_add_and_fetch(&entry->lock_index, 0);
|
||||
}
|
||||
} else {
|
||||
lock_index = entry->lock_index;
|
||||
}
|
||||
|
||||
TIMER_SET_ENTRY_STATUS_AND_SINDEX(timer, slot, entry, lock_index);
|
||||
} else {
|
||||
lock_index = TIMER_ENTRY_FETCH_LOCK_INDEX(timer, entry);
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&slot->lock);
|
||||
if ((flags & FAST_TIMER_FLAGS_SET_EXPIRES) != 0) {
|
||||
entry->expires = expires;
|
||||
}
|
||||
fc_list_add_tail(&entry->dlink, &slot->head);
|
||||
entry->rehash = false;
|
||||
|
||||
if ((flags & FAST_TIMER_FLAGS_SET_ENTRY_LOCK) == 0) {
|
||||
/* MUST set entry status and slot index in the end when entry move */
|
||||
TIMER_SET_ENTRY_STATUS_AND_SINDEX(timer, slot, entry, lock_index);
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&slot->lock);
|
||||
}
|
||||
|
||||
#define check_entry_status(timer, entry, slot_index) \
|
||||
check_set_entry_status(timer, entry, slot_index, \
|
||||
FAST_TIMER_STATUS_NONE)
|
||||
|
||||
static inline int check_set_entry_status(LockedTimer *timer,
|
||||
LockedTimerEntry *entry, int *slot_index, const int new_status)
|
||||
{
|
||||
int result;
|
||||
int lock_index;
|
||||
|
||||
lock_index = TIMER_ENTRY_FETCH_LOCK_INDEX(timer, entry);
|
||||
while (1) {
|
||||
TIMER_ENTRY_LOCK(timer, lock_index);
|
||||
switch (entry->status) {
|
||||
case FAST_TIMER_STATUS_CLEARED:
|
||||
result = ECANCELED;
|
||||
break;
|
||||
case FAST_TIMER_STATUS_TIMEOUT:
|
||||
result = ETIMEDOUT;
|
||||
break;
|
||||
case FAST_TIMER_STATUS_MOVING:
|
||||
result = EAGAIN;
|
||||
break;
|
||||
case FAST_TIMER_STATUS_NORMAL:
|
||||
result = 0;
|
||||
if (new_status != FAST_TIMER_STATUS_NONE) {
|
||||
entry->status = new_status;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = EINVAL;
|
||||
break;
|
||||
}
|
||||
*slot_index = entry->slot_index;
|
||||
TIMER_ENTRY_UNLOCK(timer, lock_index);
|
||||
|
||||
if (result != EAGAIN) {
|
||||
return result;
|
||||
}
|
||||
fc_sleep_ms(1);
|
||||
}
|
||||
}
|
||||
|
||||
void locked_timer_add_ex(LockedTimer *timer, LockedTimerEntry *entry,
|
||||
const int64_t expires, const int flags)
|
||||
{
|
||||
LockedTimerSlot *slot;
|
||||
int64_t new_expires;
|
||||
bool new_flags;
|
||||
|
||||
if (expires > timer->current_time) {
|
||||
new_expires = expires;
|
||||
new_flags = flags;
|
||||
} else {
|
||||
new_expires = timer->current_time + 1; //plus 1 for rare case
|
||||
new_flags = flags | FAST_TIMER_FLAGS_SET_EXPIRES;
|
||||
}
|
||||
slot = TIMER_GET_SLOT_POINTER(timer, new_expires);
|
||||
add_entry(timer, slot, entry, new_expires, new_flags);
|
||||
}
|
||||
|
||||
int locked_timer_modify(LockedTimer *timer, LockedTimerEntry *entry,
|
||||
const int64_t new_expires)
|
||||
{
|
||||
int result;
|
||||
int slot_index;
|
||||
|
||||
if (new_expires > entry->expires) {
|
||||
if ((result=check_entry_status(timer, entry, &slot_index)) != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&(timer->slots + slot_index)->lock);
|
||||
entry->rehash = TIMER_GET_SLOT_INDEX(timer,
|
||||
new_expires) != slot_index;
|
||||
entry->expires = new_expires; //lazy move
|
||||
PTHREAD_MUTEX_UNLOCK(&(timer->slots + slot_index)->lock);
|
||||
} else if (new_expires < entry->expires) {
|
||||
if ((result=locked_timer_remove_ex(timer, entry,
|
||||
FAST_TIMER_STATUS_MOVING)) == 0)
|
||||
{
|
||||
locked_timer_add_ex(timer, entry, new_expires,
|
||||
FAST_TIMER_FLAGS_SET_EXPIRES);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int locked_timer_remove_ex(LockedTimer *timer, LockedTimerEntry *entry,
|
||||
const int new_status)
|
||||
{
|
||||
int result;
|
||||
int slot_index;
|
||||
|
||||
if ((result=check_set_entry_status(timer, entry,
|
||||
&slot_index, new_status)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_LOCK(&(timer->slots + slot_index)->lock);
|
||||
fc_list_del_init(&entry->dlink);
|
||||
PTHREAD_MUTEX_UNLOCK(&(timer->slots + slot_index)->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int locked_timer_timeouts_get(LockedTimer *timer, const int64_t current_time,
|
||||
LockedTimerEntry *head)
|
||||
{
|
||||
LockedTimerSlot *slot;
|
||||
LockedTimerSlot *new_slot;
|
||||
LockedTimerEntry *entry;
|
||||
LockedTimerEntry *tmp;
|
||||
LockedTimerEntry *tail;
|
||||
bool is_valid;
|
||||
int lock_index;
|
||||
int count;
|
||||
|
||||
if (timer->current_time >= current_time) {
|
||||
head->next = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tail = head;
|
||||
count = 0;
|
||||
while (timer->current_time < current_time) {
|
||||
slot = TIMER_GET_SLOT_POINTER(timer, timer->current_time++);
|
||||
PTHREAD_MUTEX_LOCK(&slot->lock);
|
||||
fc_list_for_each_entry_safe(entry, tmp, &slot->head, dlink) {
|
||||
if (entry->expires >= current_time) { //not expired
|
||||
if (entry->rehash) {
|
||||
new_slot = TIMER_GET_SLOT_POINTER(timer, entry->expires);
|
||||
if (new_slot != slot) { //check to avoid deadlock
|
||||
TIMER_ENTRY_FETCH_AND_LOCK(timer, entry);
|
||||
if (entry->status == FAST_TIMER_STATUS_NORMAL) {
|
||||
entry->status = FAST_TIMER_STATUS_MOVING;
|
||||
is_valid = true;
|
||||
} else {
|
||||
is_valid = false;
|
||||
}
|
||||
TIMER_ENTRY_UNLOCK(timer, lock_index);
|
||||
|
||||
if (is_valid) {
|
||||
fc_list_del_init(&entry->dlink);
|
||||
add_entry(timer, new_slot, entry, entry->expires,
|
||||
FAST_TIMER_FLAGS_SET_NOTHING);
|
||||
}
|
||||
} else {
|
||||
entry->rehash = false;
|
||||
}
|
||||
}
|
||||
} else { //expired
|
||||
TIMER_ENTRY_FETCH_AND_LOCK(timer, entry);
|
||||
if (entry->status == FAST_TIMER_STATUS_NORMAL) {
|
||||
entry->status = FAST_TIMER_STATUS_TIMEOUT;
|
||||
is_valid = true;
|
||||
} else {
|
||||
is_valid = false;
|
||||
}
|
||||
TIMER_ENTRY_UNLOCK(timer, lock_index);
|
||||
|
||||
if (is_valid) {
|
||||
fc_list_del_init(&entry->dlink);
|
||||
tail->next = entry;
|
||||
tail = entry;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_UNLOCK(&slot->lock);
|
||||
}
|
||||
|
||||
tail->next = NULL;
|
||||
return count;
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __LOCKED_TIMER_H__
|
||||
#define __LOCKED_TIMER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <pthread.h>
|
||||
#include "common_define.h"
|
||||
#include "fc_list.h"
|
||||
|
||||
#define FAST_TIMER_STATUS_NONE 0
|
||||
#define FAST_TIMER_STATUS_NORMAL 1
|
||||
#define FAST_TIMER_STATUS_MOVING 2
|
||||
#define FAST_TIMER_STATUS_TIMEOUT 3
|
||||
#define FAST_TIMER_STATUS_CLEARED 4
|
||||
|
||||
#define FAST_TIMER_FLAGS_SET_NOTHING 0
|
||||
#define FAST_TIMER_FLAGS_SET_EXPIRES 1
|
||||
#define FAST_TIMER_FLAGS_SET_ENTRY_LOCK 2
|
||||
#define FAST_TIMER_FLAGS_SET_ALL (FAST_TIMER_FLAGS_SET_EXPIRES | \
|
||||
FAST_TIMER_FLAGS_SET_ENTRY_LOCK)
|
||||
|
||||
typedef struct locked_timer_entry {
|
||||
int64_t expires;
|
||||
struct fc_list_head dlink; //for timer slot
|
||||
struct locked_timer_entry *next; //for timeout chain
|
||||
uint32_t slot_index; //for slot lock
|
||||
volatile uint16_t lock_index; //for entry lock
|
||||
uint8_t status;
|
||||
bool rehash;
|
||||
} LockedTimerEntry;
|
||||
|
||||
typedef struct locked_timer_slot {
|
||||
struct fc_list_head head;
|
||||
pthread_mutex_t lock;
|
||||
} LockedTimerSlot;
|
||||
|
||||
typedef struct locked_timer_shared_locks {
|
||||
bool set_lock_index;
|
||||
uint16_t count;
|
||||
pthread_mutex_t *locks;
|
||||
} LockedTimerSharedLocks;
|
||||
|
||||
typedef struct locked_timer {
|
||||
int slot_count; //time wheel slot count
|
||||
LockedTimerSharedLocks entry_shares; //shared locks for entry
|
||||
int64_t base_time; //base time for slot 0
|
||||
volatile int64_t current_time;
|
||||
LockedTimerSlot *slots;
|
||||
} LockedTimer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define locked_timer_init(timer, slot_count, current_time, shared_lock_count) \
|
||||
locked_timer_init_ex(timer, slot_count, current_time, shared_lock_count, \
|
||||
true)
|
||||
|
||||
#define locked_timer_add(timer, entry) \
|
||||
locked_timer_add_ex(timer, entry, (entry)->expires, \
|
||||
FAST_TIMER_FLAGS_SET_ENTRY_LOCK)
|
||||
|
||||
#define locked_timer_remove(timer, entry) \
|
||||
locked_timer_remove_ex(timer, entry, FAST_TIMER_STATUS_CLEARED)
|
||||
|
||||
int locked_timer_init_ex(LockedTimer *timer, const int slot_count,
|
||||
const int64_t current_time, const int shared_lock_count,
|
||||
const bool set_lock_index);
|
||||
|
||||
void locked_timer_destroy(LockedTimer *timer);
|
||||
|
||||
void locked_timer_add_ex(LockedTimer *timer, LockedTimerEntry *entry,
|
||||
const int64_t expires, const int flags);
|
||||
|
||||
int locked_timer_remove_ex(LockedTimer *timer, LockedTimerEntry *entry,
|
||||
const int new_status);
|
||||
|
||||
int locked_timer_modify(LockedTimer *timer, LockedTimerEntry *entry,
|
||||
const int64_t new_expires);
|
||||
|
||||
int locked_timer_timeouts_get(LockedTimer *timer, const int64_t current_time,
|
||||
LockedTimerEntry *head);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
283
src/logger.c
283
src/logger.c
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdarg.h>
|
||||
|
|
@ -43,21 +50,21 @@ static int log_fsync(LogContext *pContext, const bool bNeedLock);
|
|||
|
||||
static int check_and_mk_log_dir(const char *base_path)
|
||||
{
|
||||
char data_path[MAX_PATH_SIZE];
|
||||
char log_path[MAX_PATH_SIZE];
|
||||
|
||||
snprintf(data_path, sizeof(data_path), "%s/logs", base_path);
|
||||
if (!fileExists(data_path))
|
||||
{
|
||||
if (mkdir(data_path, 0755) != 0)
|
||||
{
|
||||
fprintf(stderr, "mkdir \"%s\" fail, " \
|
||||
"errno: %d, error info: %s\n", \
|
||||
data_path, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : EPERM;
|
||||
}
|
||||
}
|
||||
fc_combine_full_filename(base_path, "logs", log_path);
|
||||
if (!fileExists(log_path))
|
||||
{
|
||||
if (mkdir(log_path, 0755) != 0)
|
||||
{
|
||||
fprintf(stderr, "mkdir \"%s\" fail, "
|
||||
"errno: %d, error info: %s\n",
|
||||
log_path, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : EPERM;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int log_init()
|
||||
|
|
@ -92,6 +99,7 @@ int log_init_ex(LogContext *pContext)
|
|||
pContext->log_level = LOG_INFO;
|
||||
pContext->log_fd = STDERR_FILENO;
|
||||
pContext->time_precision = LOG_TIME_PRECISION_SECOND;
|
||||
pContext->compress_log_days_before = 1;
|
||||
strcpy(pContext->rotate_time_format, "%Y%m%d_%H%M%S");
|
||||
|
||||
pContext->log_buff = (char *)malloc(LOG_BUFF_SIZE);
|
||||
|
|
@ -104,7 +112,7 @@ int log_init_ex(LogContext *pContext)
|
|||
}
|
||||
pContext->pcurrent_buff = pContext->log_buff;
|
||||
|
||||
if ((result=init_pthread_lock(&(pContext->log_thread_lock))) != 0)
|
||||
if ((result=init_pthread_lock(&(pContext->lock))) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
@ -128,8 +136,8 @@ static int log_print_header(LogContext *pContext)
|
|||
if (pContext->current_size < 0)
|
||||
{
|
||||
result = errno != 0 ? errno : EACCES;
|
||||
fprintf(stderr, "lseek file \"%s\" fail, " \
|
||||
"errno: %d, error info: %s\n", \
|
||||
fprintf(stderr, "lseek file \"%s\" fail, "
|
||||
"errno: %d, error info: %s\n",
|
||||
pContext->log_filename, result, STRERROR(result));
|
||||
}
|
||||
else {
|
||||
|
|
@ -150,8 +158,8 @@ static int log_print_header(LogContext *pContext)
|
|||
static int log_open(LogContext *pContext)
|
||||
{
|
||||
int result;
|
||||
if ((pContext->log_fd = open(pContext->log_filename, O_WRONLY | \
|
||||
O_CREAT | O_APPEND | pContext->fd_flags, 0644)) < 0)
|
||||
if ((pContext->log_fd = open(pContext->log_filename, O_WRONLY | O_CREAT |
|
||||
O_APPEND | O_CLOEXEC | pContext->fd_flags, 0644)) < 0)
|
||||
{
|
||||
fprintf(stderr, "open log file \"%s\" to write fail, " \
|
||||
"errno: %d, error info: %s\n", \
|
||||
|
|
@ -214,31 +222,44 @@ int log_reopen_ex(LogContext *pContext)
|
|||
return log_open(pContext);
|
||||
}
|
||||
|
||||
int log_set_prefix_ex(LogContext *pContext, const char *base_path, \
|
||||
int log_set_prefix_ex(LogContext *pContext, const char *base_path,
|
||||
const char *filename_prefix)
|
||||
{
|
||||
int result;
|
||||
int result;
|
||||
char log_filename[MAX_PATH_SIZE];
|
||||
|
||||
if ((result=check_and_mk_log_dir(base_path)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
if ((result=check_and_mk_log_dir(base_path)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s/logs/%s.log", \
|
||||
base_path, filename_prefix);
|
||||
|
||||
return log_open(pContext);
|
||||
snprintf(log_filename, MAX_PATH_SIZE, "%s/logs/%s.log",
|
||||
base_path, filename_prefix);
|
||||
return log_set_filename_ex(pContext, log_filename);
|
||||
}
|
||||
|
||||
int log_set_filename_ex(LogContext *pContext, const char *log_filename)
|
||||
{
|
||||
if (log_filename == NULL) {
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"log_filename is NULL!\n", __LINE__);
|
||||
if (log_filename == NULL || *log_filename == '\0')
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, "
|
||||
"log_filename is NULL or empty!\n", __LINE__);
|
||||
return EINVAL;
|
||||
}
|
||||
snprintf(pContext->log_filename, MAX_PATH_SIZE, "%s", log_filename);
|
||||
return log_open(pContext);
|
||||
|
||||
if (*(pContext->log_filename) == '\0')
|
||||
{
|
||||
fc_strlcpy(pContext->log_filename, log_filename, MAX_PATH_SIZE);
|
||||
return log_open(pContext);
|
||||
}
|
||||
|
||||
if (strcmp(log_filename, pContext->log_filename) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
fc_strlcpy(pContext->log_filename, log_filename, MAX_PATH_SIZE);
|
||||
return log_reopen_ex(pContext);
|
||||
}
|
||||
|
||||
void log_set_cache_ex(LogContext *pContext, const bool bLogCache)
|
||||
|
|
@ -275,9 +296,9 @@ void log_set_header_callback(LogContext *pContext, LogHeaderCallback header_call
|
|||
{
|
||||
int64_t current_size;
|
||||
|
||||
pthread_mutex_lock(&(pContext->log_thread_lock));
|
||||
pthread_mutex_lock(&(pContext->lock));
|
||||
current_size = pContext->current_size;
|
||||
pthread_mutex_unlock(&(pContext->log_thread_lock));
|
||||
pthread_mutex_unlock(&(pContext->lock));
|
||||
if (current_size == 0)
|
||||
{
|
||||
log_print_header(pContext);
|
||||
|
|
@ -302,7 +323,14 @@ void log_set_compress_log_flags_ex(LogContext *pContext, const short flags)
|
|||
|
||||
void log_set_compress_log_days_before_ex(LogContext *pContext, const int days_before)
|
||||
{
|
||||
pContext->compress_log_days_before = days_before;
|
||||
if (days_before > 0)
|
||||
{
|
||||
pContext->compress_log_days_before = days_before;
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->compress_log_days_before = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void log_set_fd_flags(LogContext *pContext, const int flags)
|
||||
|
|
@ -319,7 +347,7 @@ void log_destroy_ex(LogContext *pContext)
|
|||
close(pContext->log_fd);
|
||||
pContext->log_fd = STDERR_FILENO;
|
||||
|
||||
pthread_mutex_destroy(&pContext->log_thread_lock);
|
||||
pthread_mutex_destroy(&pContext->lock);
|
||||
}
|
||||
|
||||
if (pContext->log_buff != NULL)
|
||||
|
|
@ -357,12 +385,11 @@ static int log_delete_old_file(LogContext *pContext,
|
|||
char full_filename[MAX_PATH_SIZE + 128];
|
||||
if (NEED_COMPRESS_LOG(pContext->compress_log_flags))
|
||||
{
|
||||
snprintf(full_filename, sizeof(full_filename), "%s%s",
|
||||
old_filename, GZIP_EXT_NAME_STR);
|
||||
fc_concat_two_strings(old_filename, GZIP_EXT_NAME_STR, full_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(full_filename, sizeof(full_filename), "%s", old_filename);
|
||||
fc_safe_strcpy(full_filename, old_filename);
|
||||
}
|
||||
|
||||
if (unlink(full_filename) != 0)
|
||||
|
|
@ -373,6 +400,10 @@ static int log_delete_old_file(LogContext *pContext,
|
|||
"unlink %s fail, errno: %d, error info: %s\n", \
|
||||
__LINE__, full_filename, errno, STRERROR(errno));
|
||||
}
|
||||
else if (NEED_COMPRESS_LOG(pContext->compress_log_flags))
|
||||
{
|
||||
unlink(old_filename);
|
||||
}
|
||||
return errno != 0 ? errno : EPERM;
|
||||
}
|
||||
|
||||
|
|
@ -514,7 +545,9 @@ static int log_get_matched_files(LogContext *pContext,
|
|||
char *log_filename;
|
||||
char *filename;
|
||||
DIR *dir;
|
||||
#ifndef OS_LINUX
|
||||
struct dirent ent;
|
||||
#endif
|
||||
struct dirent *pEntry;
|
||||
time_t the_time;
|
||||
struct tm tm;
|
||||
|
|
@ -556,11 +589,18 @@ static int log_get_matched_files(LogContext *pContext,
|
|||
the_time = get_current_time() - days_before * 86400;
|
||||
localtime_r(&the_time, &tm);
|
||||
memset(filename_prefix, 0, sizeof(filename_prefix));
|
||||
len = sprintf(filename_prefix, "%s.", log_filename);
|
||||
len = strlen(log_filename);
|
||||
memcpy(filename_prefix, log_filename, len);
|
||||
*(filename_prefix + len++) = '.';
|
||||
strftime(filename_prefix + len, sizeof(filename_prefix) - len,
|
||||
rotate_time_format_prefix, &tm);
|
||||
prefix_filename_len = strlen(filename_prefix);
|
||||
|
||||
#ifndef OS_LINUX
|
||||
while (readdir_r(dir, &ent, &pEntry) == 0)
|
||||
#else
|
||||
while ((pEntry=readdir(dir)) != NULL)
|
||||
#endif
|
||||
{
|
||||
if (pEntry == NULL)
|
||||
{
|
||||
|
|
@ -612,8 +652,8 @@ static int log_delete_matched_old_files(LogContext *pContext,
|
|||
log_get_file_path(pContext, log_filepath);
|
||||
for (i=0; i<filename_array.count; i++)
|
||||
{
|
||||
snprintf(full_filename, sizeof(full_filename), "%s%s",
|
||||
log_filepath, filename_array.filenames[i]);
|
||||
fc_concat_two_strings(log_filepath, filename_array.
|
||||
filenames[i], full_filename);
|
||||
if (unlink(full_filename) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
|
|
@ -664,7 +704,9 @@ int log_delete_old_files(void *args)
|
|||
the_time -= 86400;
|
||||
localtime_r(&the_time, &tm);
|
||||
memset(old_filename, 0, sizeof(old_filename));
|
||||
len = sprintf(old_filename, "%s.", pContext->log_filename);
|
||||
len = strlen(pContext->log_filename);
|
||||
memcpy(old_filename, pContext->log_filename, len);
|
||||
*(old_filename + len++) = '.';
|
||||
strftime(old_filename + len, sizeof(old_filename) - len,
|
||||
pContext->rotate_time_format, &tm);
|
||||
if ((result=log_delete_old_file(pContext, old_filename)) != 0)
|
||||
|
|
@ -686,31 +728,20 @@ int log_delete_old_files(void *args)
|
|||
}
|
||||
}
|
||||
|
||||
static void* log_gzip_func(void *args)
|
||||
static void *log_gzip_func(void *args)
|
||||
{
|
||||
LogContext *pContext;
|
||||
char *gzip;
|
||||
char cmd[MAX_PATH_SIZE + 128];
|
||||
struct log_filename_array filename_array;
|
||||
char log_filepath[MAX_PATH_SIZE];
|
||||
char full_filename[MAX_PATH_SIZE + 32];
|
||||
char output[512];
|
||||
const char *gzip_cmd_filename;
|
||||
int prefix_len;
|
||||
int result;
|
||||
int i;
|
||||
|
||||
pContext = (LogContext *)args;
|
||||
if (access("/bin/gzip", F_OK) == 0)
|
||||
{
|
||||
gzip = "/bin/gzip";
|
||||
}
|
||||
else if (access("/usr/bin/gzip", F_OK) == 0)
|
||||
{
|
||||
gzip = "/usr/bin/gzip";
|
||||
}
|
||||
else
|
||||
{
|
||||
gzip = "gzip";
|
||||
}
|
||||
|
||||
if (log_get_prefix_len(pContext, &prefix_len) != 0)
|
||||
{
|
||||
return NULL;
|
||||
|
|
@ -733,13 +764,25 @@ static void* log_gzip_func(void *args)
|
|||
continue;
|
||||
}
|
||||
|
||||
snprintf(full_filename, sizeof(full_filename), "%s%s",
|
||||
log_filepath, filename_array.filenames[i]);
|
||||
snprintf(cmd, sizeof(cmd), "%s %s", gzip, full_filename);
|
||||
if (system(cmd) == -1)
|
||||
{
|
||||
fprintf(stderr, "execute %s fail\n", cmd);
|
||||
}
|
||||
gzip_cmd_filename = get_gzip_command_filename();
|
||||
fc_concat_two_strings(log_filepath, filename_array.
|
||||
filenames[i], full_filename);
|
||||
fc_combine_two_strings(gzip_cmd_filename, full_filename, ' ', cmd);
|
||||
result = getExecResult(cmd, output, sizeof(output));
|
||||
if (result != 0)
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, "
|
||||
"exec command \"%s\" fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, cmd, result, STRERROR(result));
|
||||
break;
|
||||
}
|
||||
if (*output != '\0')
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, "
|
||||
"exec command \"%s\", output: %s",
|
||||
__LINE__, cmd, output);
|
||||
}
|
||||
}
|
||||
|
||||
log_free_filename_array(&filename_array);
|
||||
|
|
@ -790,8 +833,7 @@ int log_rotate(LogContext *pContext)
|
|||
|
||||
close(pContext->log_fd);
|
||||
|
||||
current_time = get_current_time();
|
||||
localtime_r(¤t_time, &tm);
|
||||
current_time = get_current_time();
|
||||
if (tm.tm_hour == 0 && tm.tm_min <= 1)
|
||||
{
|
||||
if (strstr(pContext->rotate_time_format, "%H") == NULL
|
||||
|
|
@ -799,12 +841,14 @@ int log_rotate(LogContext *pContext)
|
|||
&& strstr(pContext->rotate_time_format, "%S") == NULL)
|
||||
{
|
||||
current_time -= 120;
|
||||
localtime_r(¤t_time, &tm);
|
||||
}
|
||||
}
|
||||
localtime_r(¤t_time, &tm);
|
||||
|
||||
memset(old_filename, 0, sizeof(old_filename));
|
||||
len = sprintf(old_filename, "%s.", pContext->log_filename);
|
||||
len = strlen(pContext->log_filename);
|
||||
memcpy(old_filename, pContext->log_filename, len);
|
||||
*(old_filename + len++) = '.';
|
||||
strftime(old_filename + len, sizeof(old_filename) - len,
|
||||
pContext->rotate_time_format, &tm);
|
||||
if (access(old_filename, F_OK) == 0)
|
||||
|
|
@ -874,19 +918,19 @@ static int log_fsync(LogContext *pContext, const bool bNeedLock)
|
|||
{
|
||||
if (bNeedLock)
|
||||
{
|
||||
pthread_mutex_lock(&(pContext->log_thread_lock));
|
||||
pthread_mutex_lock(&(pContext->lock));
|
||||
}
|
||||
result = log_check_rotate(pContext);
|
||||
if (bNeedLock)
|
||||
{
|
||||
pthread_mutex_unlock(&(pContext->log_thread_lock));
|
||||
pthread_mutex_unlock(&(pContext->lock));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (bNeedLock && ((lock_res=pthread_mutex_lock( \
|
||||
&(pContext->log_thread_lock))) != 0))
|
||||
&(pContext->lock))) != 0))
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
|
|
@ -911,10 +955,10 @@ static int log_fsync(LogContext *pContext, const bool bNeedLock)
|
|||
if (written != write_bytes)
|
||||
{
|
||||
result = errno != 0 ? errno : EIO;
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"call write fail, errno: %d, error info: %s\n",\
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, "
|
||||
"pid: %d, call write fail, fd: %d, errno: %d, error info: %s\n",
|
||||
__LINE__, getpid(), pContext->log_fd, result, STRERROR(result));
|
||||
}
|
||||
|
||||
if (pContext->rotate_immediately)
|
||||
{
|
||||
|
|
@ -922,7 +966,7 @@ static int log_fsync(LogContext *pContext, const bool bNeedLock)
|
|||
}
|
||||
|
||||
if (bNeedLock && ((lock_res=pthread_mutex_unlock( \
|
||||
&(pContext->log_thread_lock))) != 0))
|
||||
&(pContext->lock))) != 0))
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
|
|
@ -933,8 +977,8 @@ static int log_fsync(LogContext *pContext, const bool bNeedLock)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
||||
const char *caption, const char *text, const int text_len, \
|
||||
void log_it_ex3(LogContext *pContext, struct timeval *tv,
|
||||
const char *caption, const char *text, const int text_len,
|
||||
const bool bNeedSync, const bool bNeedLock)
|
||||
{
|
||||
struct tm tm;
|
||||
|
|
@ -959,7 +1003,7 @@ static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
|||
}
|
||||
}
|
||||
|
||||
if (bNeedLock && (result=pthread_mutex_lock(&pContext->log_thread_lock)) != 0)
|
||||
if (bNeedLock && (result=pthread_mutex_lock(&pContext->lock)) != 0)
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
|
|
@ -974,12 +1018,12 @@ static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
|||
__LINE__, LOG_BUFF_SIZE, text_len + 64);
|
||||
if (bNeedLock)
|
||||
{
|
||||
pthread_mutex_unlock(&(pContext->log_thread_lock));
|
||||
pthread_mutex_unlock(&(pContext->lock));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ((pContext->pcurrent_buff - pContext->log_buff) + text_len + 64 \
|
||||
if ((pContext->pcurrent_buff - pContext->log_buff) + text_len + 64
|
||||
> LOG_BUFF_SIZE)
|
||||
{
|
||||
log_fsync(pContext, false);
|
||||
|
|
@ -1006,10 +1050,14 @@ static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
|||
}
|
||||
|
||||
if (caption != NULL)
|
||||
{
|
||||
buff_len = sprintf(pContext->pcurrent_buff, "%s - ", caption);
|
||||
pContext->pcurrent_buff += buff_len;
|
||||
}
|
||||
{
|
||||
buff_len = strlen(caption);
|
||||
memcpy(pContext->pcurrent_buff, caption, buff_len);
|
||||
pContext->pcurrent_buff += buff_len;
|
||||
*pContext->pcurrent_buff++ = ' ';
|
||||
*pContext->pcurrent_buff++ = '-';
|
||||
*pContext->pcurrent_buff++ = ' ';
|
||||
}
|
||||
memcpy(pContext->pcurrent_buff, text, text_len);
|
||||
pContext->pcurrent_buff += text_len;
|
||||
*pContext->pcurrent_buff++ = '\n';
|
||||
|
|
@ -1019,7 +1067,7 @@ static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
|||
log_fsync(pContext, false);
|
||||
}
|
||||
|
||||
if (bNeedLock && (result=pthread_mutex_unlock(&(pContext->log_thread_lock))) != 0)
|
||||
if (bNeedLock && (result=pthread_mutex_unlock(&(pContext->lock))) != 0)
|
||||
{
|
||||
fprintf(stderr, "file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
|
|
@ -1028,8 +1076,8 @@ static void doLogEx(LogContext *pContext, struct timeval *tv, \
|
|||
}
|
||||
}
|
||||
|
||||
void log_it_ex2(LogContext *pContext, const char *caption, \
|
||||
const char *text, const int text_len, \
|
||||
void log_it_ex2(LogContext *pContext, const char *caption,
|
||||
const char *text, const int text_len,
|
||||
const bool bNeedSync, const bool bNeedLock)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
|
@ -1044,10 +1092,10 @@ void log_it_ex2(LogContext *pContext, const char *caption, \
|
|||
gettimeofday(&tv, NULL);
|
||||
}
|
||||
|
||||
doLogEx(pContext, &tv, caption, text, text_len, bNeedSync, bNeedLock);
|
||||
log_it_ex3(pContext, &tv, caption, text, text_len, bNeedSync, bNeedLock);
|
||||
}
|
||||
|
||||
void log_it_ex1(LogContext *pContext, const int priority, \
|
||||
void log_it_ex1(LogContext *pContext, const int priority,
|
||||
const char *text, const int text_len)
|
||||
{
|
||||
bool bNeedSync;
|
||||
|
|
@ -1243,45 +1291,12 @@ void logAccess(LogContext *pContext, struct timeval *tvStart, \
|
|||
{
|
||||
len = sizeof(text) - 1;
|
||||
}
|
||||
doLogEx(pContext, tvStart, NULL, text, len, false, true);
|
||||
log_it_ex3(pContext, tvStart, NULL, text, len, false, true);
|
||||
}
|
||||
|
||||
const char *log_get_level_caption_ex(LogContext *pContext)
|
||||
{
|
||||
const char *caption;
|
||||
|
||||
switch (pContext->log_level)
|
||||
{
|
||||
case LOG_DEBUG:
|
||||
caption = "DEBUG";
|
||||
break;
|
||||
case LOG_INFO:
|
||||
caption = "INFO";
|
||||
break;
|
||||
case LOG_NOTICE:
|
||||
caption = "NOTICE";
|
||||
break;
|
||||
case LOG_WARNING:
|
||||
caption = "WARNING";
|
||||
break;
|
||||
case LOG_ERR:
|
||||
caption = "ERROR";
|
||||
break;
|
||||
case LOG_CRIT:
|
||||
caption = "CRIT";
|
||||
break;
|
||||
case LOG_ALERT:
|
||||
caption = "ALERT";
|
||||
break;
|
||||
case LOG_EMERG:
|
||||
caption = "EMERG";
|
||||
break;
|
||||
default:
|
||||
caption = "UNKOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
return caption;
|
||||
return get_log_level_caption(pContext->log_level);
|
||||
}
|
||||
|
||||
#ifndef LOG_FORMAT_CHECK
|
||||
|
|
|
|||
65
src/logger.h
65
src/logger.h
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//logger.h
|
||||
#ifndef LOGGER_H
|
||||
|
|
@ -29,11 +36,16 @@ extern "C" {
|
|||
#define LOG_COMPRESS_FLAGS_ENABLED 1
|
||||
#define LOG_COMPRESS_FLAGS_NEW_THREAD 2
|
||||
|
||||
#define LOG_NOTHING (LOG_DEBUG + 10)
|
||||
|
||||
struct log_context;
|
||||
|
||||
//log header line callback
|
||||
typedef void (*LogHeaderCallback)(struct log_context *pContext);
|
||||
|
||||
#define FC_LOG_BY_LEVEL(level) \
|
||||
(level <= g_log_context.log_level)
|
||||
|
||||
typedef struct log_context
|
||||
{
|
||||
/* log level value please see: sys/syslog.h
|
||||
|
|
@ -50,7 +62,7 @@ typedef struct log_context
|
|||
char *pcurrent_buff;
|
||||
|
||||
/* mutext lock */
|
||||
pthread_mutex_t log_thread_lock;
|
||||
pthread_mutex_t lock;
|
||||
|
||||
/*
|
||||
rotate the log when the log file exceeds this parameter
|
||||
|
|
@ -133,6 +145,7 @@ static inline int log_try_init()
|
|||
*/
|
||||
int log_init2();
|
||||
|
||||
|
||||
#define log_reopen() log_reopen_ex(&g_log_context)
|
||||
|
||||
#define log_set_prefix(base_path, filename_prefix) \
|
||||
|
|
@ -159,6 +172,13 @@ int log_init2();
|
|||
|
||||
#define log_destroy() log_destroy_ex(&g_log_context)
|
||||
|
||||
#define log_it1(priority, text, text_len) \
|
||||
log_it_ex1(&g_log_context, priority, text, text_len)
|
||||
|
||||
#define log_it2(caption, text, text_len, bNeedSync, bNeedLock) \
|
||||
log_it_ex2(&g_log_context, caption, text, text_len, bNeedSync, bNeedLock)
|
||||
|
||||
|
||||
/** init function, use stderr for output by default
|
||||
* parameters:
|
||||
* pContext: the log context
|
||||
|
|
@ -181,7 +201,7 @@ int log_reopen_ex(LogContext *pContext);
|
|||
* filename_prefix: log filename prefix
|
||||
* return: 0 for success, != 0 fail
|
||||
*/
|
||||
int log_set_prefix_ex(LogContext *pContext, const char *base_path, \
|
||||
int log_set_prefix_ex(LogContext *pContext, const char *base_path,
|
||||
const char *filename_prefix);
|
||||
|
||||
/** set log filename
|
||||
|
|
@ -254,6 +274,24 @@ void log_take_over_stderr_ex(LogContext *pContext);
|
|||
*/
|
||||
void log_take_over_stdout_ex(LogContext *pContext);
|
||||
|
||||
|
||||
/** init function using global log context
|
||||
* do nothing when already inited
|
||||
* return: 0 for success, != 0 fail
|
||||
*/
|
||||
static inline int log_try_init2()
|
||||
{
|
||||
int result;
|
||||
if ((result=log_try_init()) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
log_take_over_stderr();
|
||||
log_take_over_stdout();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** set compress_log_flags to true
|
||||
* parameters:
|
||||
* pContext: the log context
|
||||
|
|
@ -305,7 +343,7 @@ void log_it_ex(LogContext *pContext, const int priority, \
|
|||
* text_len: text string length (bytes)
|
||||
* return: none
|
||||
*/
|
||||
void log_it_ex1(LogContext *pContext, const int priority, \
|
||||
void log_it_ex1(LogContext *pContext, const int priority,
|
||||
const char *text, const int text_len);
|
||||
|
||||
/** log to file
|
||||
|
|
@ -317,10 +355,13 @@ void log_it_ex1(LogContext *pContext, const int priority, \
|
|||
* bNeedSync: if sync to file immediatelly
|
||||
* return: none
|
||||
*/
|
||||
void log_it_ex2(LogContext *pContext, const char *caption, \
|
||||
const char *text, const int text_len, \
|
||||
void log_it_ex2(LogContext *pContext, const char *caption,
|
||||
const char *text, const int text_len,
|
||||
const bool bNeedSync, const bool bNeedLock);
|
||||
|
||||
void log_it_ex3(LogContext *pContext, struct timeval *tv,
|
||||
const char *caption, const char *text, const int text_len,
|
||||
const bool bNeedSync, const bool bNeedLock);
|
||||
|
||||
/** sync log buffer to log file
|
||||
* parameters:
|
||||
|
|
|
|||
27
src/md5.c
27
src/md5.c
|
|
@ -311,7 +311,7 @@ MD5_memset(POINTER output, int value, unsigned int len)
|
|||
/*
|
||||
* Digests a string
|
||||
*/
|
||||
int my_md5_string(char *string,unsigned char digest[16])
|
||||
int my_md5_string(char *string, unsigned char digest[16])
|
||||
{
|
||||
MD5_CTX context;
|
||||
unsigned int len = strlen(string);
|
||||
|
|
@ -332,25 +332,22 @@ int my_md5_buffer(char *buffer, unsigned int len, unsigned char digest[16])
|
|||
return 0;
|
||||
}
|
||||
|
||||
int my_md5_file(char *filename,unsigned char digest[16])
|
||||
int my_md5_file(char *filename, unsigned char digest[16])
|
||||
{
|
||||
FILE *file;
|
||||
MD5_CTX context;
|
||||
int len;
|
||||
unsigned char buffer[1024];
|
||||
unsigned char buff[16 * 1024];
|
||||
|
||||
if ((file = fopen(filename, "rb")) == NULL)
|
||||
if ((file = fopen(filename, "rb")) == NULL) {
|
||||
return -1;
|
||||
else {
|
||||
my_md5_init(&context);
|
||||
while ((len = fread(buffer, 1, 1024, file)) > 0)
|
||||
{
|
||||
my_md5_update(&context, buffer, len);
|
||||
}
|
||||
my_md5_final(digest, &context);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
return 0;
|
||||
my_md5_init(&context);
|
||||
while ((len = fread(buff, 1, sizeof(buff), file)) > 0) {
|
||||
my_md5_update(&context, buff, len);
|
||||
}
|
||||
my_md5_final(digest, &context);
|
||||
fclose(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -41,11 +41,12 @@ int my_md5_file(char *filename, unsigned char digest[16]);
|
|||
*/
|
||||
int my_md5_buffer(char *buffer, unsigned int len, unsigned char digest[16]);
|
||||
|
||||
void my_md5_init (MD5_CTX *);
|
||||
void my_md5_init(MD5_CTX *context);
|
||||
|
||||
void my_md5_update (MD5_CTX *, unsigned char *, unsigned int);
|
||||
void my_md5_update(MD5_CTX *context, unsigned char *input,
|
||||
unsigned int inputLen);
|
||||
|
||||
void my_md5_final (unsigned char [16], MD5_CTX *);
|
||||
void my_md5_final(unsigned char digest[16], MD5_CTX *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2015 Happy Fish / YuQing
|
||||
*
|
||||
* libfastcommon may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//multi_skiplist.c
|
||||
|
||||
|
|
@ -15,6 +22,7 @@
|
|||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include "logger.h"
|
||||
#include "fc_memory.h"
|
||||
#include "multi_skiplist.h"
|
||||
|
||||
int multi_skiplist_init_ex(MultiSkiplist *sl, const int level_count,
|
||||
|
|
@ -22,6 +30,8 @@ int multi_skiplist_init_ex(MultiSkiplist *sl, const int level_count,
|
|||
skiplist_free_func free_func,
|
||||
const int min_alloc_elements_once)
|
||||
{
|
||||
const int64_t alloc_elements_limit = 0;
|
||||
char name[64];
|
||||
int bytes;
|
||||
int element_size;
|
||||
int i;
|
||||
|
|
@ -44,21 +54,15 @@ int multi_skiplist_init_ex(MultiSkiplist *sl, const int level_count,
|
|||
}
|
||||
|
||||
bytes = sizeof(MultiSkiplistNode *) * level_count;
|
||||
sl->tmp_previous = (MultiSkiplistNode **)malloc(bytes);
|
||||
sl->tmp_previous = (MultiSkiplistNode **)fc_malloc(bytes);
|
||||
if (sl->tmp_previous == NULL) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
bytes = sizeof(struct fast_mblock_man) * level_count;
|
||||
sl->mblocks = (struct fast_mblock_man *)malloc(bytes);
|
||||
sl->mblocks = (struct fast_mblock_man *)fc_malloc(bytes);
|
||||
if (sl->mblocks == NULL) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"malloc %d bytes fail, errno: %d, error info: %s",
|
||||
__LINE__, bytes, errno, STRERROR(errno));
|
||||
return errno != 0 ? errno : ENOMEM;
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(sl->mblocks, 0, bytes);
|
||||
|
||||
|
|
@ -71,9 +75,12 @@ int multi_skiplist_init_ex(MultiSkiplist *sl, const int level_count,
|
|||
}
|
||||
|
||||
for (i=level_count-1; i>=0; i--) {
|
||||
element_size = sizeof(MultiSkiplistNode) + sizeof(MultiSkiplistNode *) * (i + 1);
|
||||
if ((result=fast_mblock_init_ex(sl->mblocks + i,
|
||||
element_size, alloc_elements_once, NULL, false)) != 0)
|
||||
sprintf(name, "multi-sl-level%02d", i);
|
||||
element_size = sizeof(MultiSkiplistNode) +
|
||||
sizeof(MultiSkiplistNode *) * (i + 1);
|
||||
if ((result=fast_mblock_init_ex1(sl->mblocks + i, name,
|
||||
element_size, alloc_elements_once, alloc_elements_limit,
|
||||
NULL, NULL, false)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
@ -96,9 +103,9 @@ int multi_skiplist_init_ex(MultiSkiplist *sl, const int level_count,
|
|||
}
|
||||
memset(sl->tail, 0, sl->mblocks[0].info.element_size);
|
||||
|
||||
if ((result=fast_mblock_init_ex(&sl->data_mblock,
|
||||
if ((result=fast_mblock_init_ex1(&sl->data_mblock, "multi-sl-data",
|
||||
sizeof(MultiSkiplistData), alloc_elements_once,
|
||||
NULL, false)) != 0)
|
||||
alloc_elements_limit, NULL, NULL, false)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
|
@ -405,6 +412,16 @@ int multi_skiplist_find_all(MultiSkiplist *sl, void *data,
|
|||
}
|
||||
}
|
||||
|
||||
void *multi_skiplist_find_ge(MultiSkiplist *sl, void *data)
|
||||
{
|
||||
MultiSkiplistNode *node;
|
||||
node = multi_skiplist_get_first_larger_or_equal(sl, data);
|
||||
if (node == sl->tail) {
|
||||
return NULL;
|
||||
}
|
||||
return node->head->data;
|
||||
}
|
||||
|
||||
int multi_skiplist_find_range(MultiSkiplist *sl, void *start_data, void *end_data,
|
||||
MultiSkiplistIterator *iterator)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2015 Happy Fish / YuQing
|
||||
*
|
||||
* libfastcommon may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//multi_skiplist.h, support duplicated entries, and support stable sort :)
|
||||
|
||||
|
|
@ -75,6 +82,7 @@ int multi_skiplist_find_all(MultiSkiplist *sl, void *data,
|
|||
MultiSkiplistIterator *iterator);
|
||||
int multi_skiplist_find_range(MultiSkiplist *sl, void *start_data, void *end_data,
|
||||
MultiSkiplistIterator *iterator);
|
||||
void *multi_skiplist_find_ge(MultiSkiplist *sl, void *data);
|
||||
|
||||
static inline void multi_skiplist_iterator(MultiSkiplist *sl,
|
||||
MultiSkiplistIterator *iterator)
|
||||
|
|
@ -109,6 +117,15 @@ static inline void *multi_skiplist_next(MultiSkiplistIterator *iterator)
|
|||
return data;
|
||||
}
|
||||
|
||||
static inline void *multi_skiplist_get_first(MultiSkiplist *sl)
|
||||
{
|
||||
if (sl->top->links[0] != sl->tail) {
|
||||
return sl->top->links[0]->head->data;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool multi_skiplist_empty(MultiSkiplist *sl)
|
||||
{
|
||||
return sl->top->links[0] == sl->tail;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "common_define.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
@ -19,12 +34,19 @@
|
|||
static int fast_multi_sock_client_do_recv(FastMultiSockClient *client,
|
||||
FastMultiSockEntry *entry);
|
||||
|
||||
int fast_multi_sock_client_init(FastMultiSockClient *client,
|
||||
static int64_t fms_get_current_time_ms()
|
||||
{
|
||||
return (int64_t)get_current_time() * 1000LL;
|
||||
}
|
||||
|
||||
int fast_multi_sock_client_init_ex(FastMultiSockClient *client,
|
||||
FastMultiSockEntry *entries, const int entry_count,
|
||||
const int header_length,
|
||||
fast_multi_sock_client_get_body_length_func get_body_length_func,
|
||||
const int init_recv_buffer_size, const int timeout)
|
||||
fms_client_get_body_length_func get_body_length_func,
|
||||
fms_client_get_current_time_ms_func get_current_time_ms_func,
|
||||
const int init_recv_buffer_size, const int timeout_ms)
|
||||
{
|
||||
const bool use_io_uring = false;
|
||||
int result;
|
||||
int new_init_recv_buffer_size;
|
||||
int i;
|
||||
|
|
@ -44,8 +66,8 @@ int fast_multi_sock_client_init(FastMultiSockClient *client,
|
|||
return EINVAL;
|
||||
}
|
||||
|
||||
if ((result=ioevent_init(&client->ioevent, entry_count,
|
||||
timeout * 1000, 0)) != 0)
|
||||
if ((result=ioevent_init(&client->ioevent, "client",
|
||||
use_io_uring, entry_count, timeout_ms, 0)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"ioevent_init fail, errno: %d, error info: %s",
|
||||
|
|
@ -64,7 +86,7 @@ int fast_multi_sock_client_init(FastMultiSockClient *client,
|
|||
}
|
||||
|
||||
for (i=0; i<entry_count; i++) {
|
||||
if ((result=fast_buffer_init_ex(&entries[i].recv_buffer,
|
||||
if ((result=fast_buffer_init1(&entries[i].recv_buffer,
|
||||
new_init_recv_buffer_size)) != 0)
|
||||
{
|
||||
return result;
|
||||
|
|
@ -74,12 +96,24 @@ int fast_multi_sock_client_init(FastMultiSockClient *client,
|
|||
client->entry_count = entry_count;
|
||||
client->header_length = header_length;
|
||||
client->get_body_length_func = get_body_length_func;
|
||||
client->get_current_time_ms_func = get_current_time_ms_func;
|
||||
client->entries = entries;
|
||||
client->timeout = timeout;
|
||||
client->timeout_ms = timeout_ms;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fast_multi_sock_client_init(FastMultiSockClient *client,
|
||||
FastMultiSockEntry *entries, const int entry_count,
|
||||
const int header_length,
|
||||
fms_client_get_body_length_func get_body_length_func,
|
||||
const int init_recv_buffer_size, const int timeout)
|
||||
{
|
||||
return fast_multi_sock_client_init_ex(client, entries, entry_count,
|
||||
header_length, get_body_length_func, fms_get_current_time_ms,
|
||||
init_recv_buffer_size, timeout * 1000);
|
||||
}
|
||||
|
||||
void fast_multi_sock_client_destroy(FastMultiSockClient *client)
|
||||
{
|
||||
int i;
|
||||
|
|
@ -95,6 +129,7 @@ static int fast_multi_sock_client_do_send(FastMultiSockClient *client,
|
|||
{
|
||||
int bytes;
|
||||
int result;
|
||||
char formatted_ip[FORMATTED_IP_SIZE];
|
||||
|
||||
result = 0;
|
||||
while (entry->remain > 0) {
|
||||
|
|
@ -104,32 +139,28 @@ static int fast_multi_sock_client_do_send(FastMultiSockClient *client,
|
|||
if (bytes < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
break;
|
||||
}
|
||||
else if (errno == EINTR) { //should retry
|
||||
} else if (errno == EINTR) { //should retry
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"server: %s:%d, ignore interupt signal",
|
||||
__LINE__, entry->conn->ip_addr,
|
||||
"server: %s:%u, ignore interupt signal", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result = errno != 0 ? errno : ECONNRESET;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"send to server %s:%d fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, entry->conn->ip_addr,
|
||||
entry->conn->port,
|
||||
result, strerror(result));
|
||||
"send to server %s:%u fail, "
|
||||
"errno: %d, error info: %s", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port, result, strerror(result));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (bytes == 0) {
|
||||
} else if (bytes == 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"send to server %s:%d, sock: %d fail, "
|
||||
"connection disconnected",
|
||||
__LINE__, entry->conn->ip_addr, entry->conn->port,
|
||||
entry->conn->sock);
|
||||
"send to server %s:%u, sock: %d fail, "
|
||||
"connection disconnected", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port, entry->conn->sock);
|
||||
|
||||
result = ECONNRESET;
|
||||
break;
|
||||
|
|
@ -138,7 +169,7 @@ static int fast_multi_sock_client_do_send(FastMultiSockClient *client,
|
|||
entry->remain -= bytes;
|
||||
if (entry->remain == 0) {
|
||||
entry->remain = client->header_length; //to recv pkg header
|
||||
entry->recv_stage = fast_multi_sock_stage_recv_header;
|
||||
entry->recv_stage = fms_stage_recv_header;
|
||||
entry->io_callback = fast_multi_sock_client_do_recv;
|
||||
if (ioevent_modify(&client->ioevent, entry->conn->sock,
|
||||
IOEVENT_READ, entry) != 0)
|
||||
|
|
@ -160,6 +191,7 @@ static int fast_multi_sock_client_send_data(FastMultiSockClient *client,
|
|||
{
|
||||
int i;
|
||||
int result;
|
||||
char formatted_ip[FORMATTED_IP_SIZE];
|
||||
|
||||
for (i=0; i<client->entry_count; i++) {
|
||||
client->entries[i].remain = send_buffer->length;
|
||||
|
|
@ -172,9 +204,9 @@ static int fast_multi_sock_client_send_data(FastMultiSockClient *client,
|
|||
client->entries[i].error_no = ENOTCONN;
|
||||
client->entries[i].done = true;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"NOT connected to %s:%d",
|
||||
__LINE__, client->entries[i].conn->ip_addr,
|
||||
client->entries[i].conn->port);
|
||||
"NOT connected to %s:%u", __LINE__,
|
||||
format_ip_address(client->entries[i].conn->ip_addr,
|
||||
formatted_ip), client->entries[i].conn->port);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -214,6 +246,7 @@ static int fast_multi_sock_client_do_recv(FastMultiSockClient *client,
|
|||
{
|
||||
int bytes;
|
||||
int result;
|
||||
char formatted_ip[FORMATTED_IP_SIZE];
|
||||
|
||||
result = 0;
|
||||
while (entry->remain > 0) {
|
||||
|
|
@ -222,32 +255,28 @@ static int fast_multi_sock_client_do_recv(FastMultiSockClient *client,
|
|||
if (bytes < 0) {
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||
break;
|
||||
}
|
||||
else if (errno == EINTR) { //should retry
|
||||
} else if (errno == EINTR) { //should retry
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"server: %s:%d, ignore interupt signal",
|
||||
__LINE__, entry->conn->ip_addr,
|
||||
"server: %s:%u, ignore interupt signal", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result = errno != 0 ? errno : ECONNRESET;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"server: %s:%d, recv failed, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, entry->conn->ip_addr,
|
||||
entry->conn->port,
|
||||
result, strerror(result));
|
||||
"server: %s:%u, recv failed, "
|
||||
"errno: %d, error info: %s", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port, result, strerror(result));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (bytes == 0) {
|
||||
} else if (bytes == 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"server: %s:%d, sock: %d, recv failed, "
|
||||
"connection disconnected",
|
||||
__LINE__, entry->conn->ip_addr, entry->conn->port,
|
||||
entry->conn->sock);
|
||||
"server: %s:%u, sock: %d, recv failed, "
|
||||
"connection disconnected", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port, entry->conn->sock);
|
||||
|
||||
result = ECONNRESET;
|
||||
break;
|
||||
|
|
@ -255,24 +284,25 @@ static int fast_multi_sock_client_do_recv(FastMultiSockClient *client,
|
|||
|
||||
entry->recv_buffer.length += bytes;
|
||||
entry->remain -= bytes;
|
||||
if (entry->remain == 0 && entry->recv_stage == fast_multi_sock_stage_recv_header) {
|
||||
if (entry->remain == 0 && entry->recv_stage == fms_stage_recv_header) {
|
||||
int body_length;
|
||||
|
||||
entry->recv_stage = fast_multi_sock_stage_recv_body;
|
||||
entry->recv_stage = fms_stage_recv_body;
|
||||
body_length = client->get_body_length_func(&entry->recv_buffer);
|
||||
if (body_length < 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"server: %s:%d, body_length: %d < 0",
|
||||
__LINE__, entry->conn->ip_addr,
|
||||
"server: %s:%u, body_length: %d < 0", __LINE__,
|
||||
format_ip_address(entry->conn->ip_addr, formatted_ip),
|
||||
entry->conn->port, body_length);
|
||||
result = EINVAL;
|
||||
result = EPIPE;
|
||||
break;
|
||||
}
|
||||
else if (body_length == 0) {
|
||||
} else if (body_length == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((result=fast_buffer_check(&entry->recv_buffer, body_length)) != 0) {
|
||||
if ((result=fast_buffer_check(&entry->recv_buffer,
|
||||
body_length)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
entry->remain = body_length; //to recv body
|
||||
|
|
@ -287,41 +317,80 @@ static int fast_multi_sock_client_do_recv(FastMultiSockClient *client,
|
|||
static int fast_multi_sock_client_deal_io(FastMultiSockClient *client)
|
||||
{
|
||||
int result;
|
||||
int event;
|
||||
int count;
|
||||
#if IOEVENT_USE_URING
|
||||
unsigned head;
|
||||
#else
|
||||
int event;
|
||||
int index;
|
||||
time_t remain_timeout;
|
||||
#endif
|
||||
int remain_timeout;
|
||||
FastMultiSockEntry *entry;
|
||||
char formatted_ip[FORMATTED_IP_SIZE];
|
||||
|
||||
while (client->pulling_count > 0) {
|
||||
remain_timeout = client->deadline_time - get_current_time();
|
||||
remain_timeout = client->deadline_time_ms -
|
||||
client->get_current_time_ms_func();
|
||||
if (remain_timeout < 0) { //timeout
|
||||
break;
|
||||
}
|
||||
|
||||
count = ioevent_poll_ex(&client->ioevent, remain_timeout * 1000);
|
||||
logInfo("poll count: %d\n", count);
|
||||
#if IOEVENT_USE_URING
|
||||
result = io_uring_wait_cqe_timeout(&client->ioevent.ring,
|
||||
&client->ioevent.cqe, &client->ioevent.timeout);
|
||||
switch (result) {
|
||||
case 0:
|
||||
break;
|
||||
case -ETIME:
|
||||
case -EAGAIN:
|
||||
case -EINTR:
|
||||
continue;
|
||||
default:
|
||||
result *= -1;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"io_uring_wait_cqe fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
io_uring_for_each_cqe(&client->ioevent.ring, head, client->ioevent.cqe) {
|
||||
count++;
|
||||
entry = (FastMultiSockEntry *)client->ioevent.cqe->user_data;
|
||||
//logInfo("sock: %d, event: %d", entry->conn->sock, event);
|
||||
result = entry->io_callback(client, entry);
|
||||
if (result != 0 || entry->remain == 0) {
|
||||
fast_multi_sock_client_finish(client, entry, result);
|
||||
}
|
||||
}
|
||||
io_uring_cq_advance(&client->ioevent.ring, count);
|
||||
|
||||
#else
|
||||
count = ioevent_poll_ex(&client->ioevent, remain_timeout);
|
||||
//logInfo("poll count: %d\n", count);
|
||||
for (index=0; index<count; index++) {
|
||||
event = IOEVENT_GET_EVENTS(&client->ioevent, index);
|
||||
entry = (FastMultiSockEntry *)IOEVENT_GET_DATA(&client->ioevent, index);
|
||||
entry = (FastMultiSockEntry *)IOEVENT_GET_DATA(
|
||||
&client->ioevent, index);
|
||||
|
||||
if (event & IOEVENT_ERROR) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"server: %s:%d, recv error event: %d, "
|
||||
"connection reset", __LINE__,
|
||||
entry->conn->ip_addr, entry->conn->port, event);
|
||||
"server: %s:%u, recv error event: %d, connection "
|
||||
"reset", __LINE__, format_ip_address(entry->conn->
|
||||
ip_addr, formatted_ip), entry->conn->port, event);
|
||||
|
||||
fast_multi_sock_client_finish(client, entry, ECONNRESET);
|
||||
continue;
|
||||
}
|
||||
|
||||
logInfo("sock: %d, event: %d", entry->conn->sock, event);
|
||||
//logInfo("sock: %d, event: %d", entry->conn->sock, event);
|
||||
|
||||
result = entry->io_callback(client, entry);
|
||||
if (result != 0 || entry->remain == 0) {
|
||||
fast_multi_sock_client_finish(client, entry, result);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -333,11 +402,12 @@ static int fast_multi_sock_client_deal_io(FastMultiSockClient *client)
|
|||
int i;
|
||||
for (i=0; i<client->entry_count; i++) {
|
||||
if (!client->entries[i].done) {
|
||||
fast_multi_sock_client_finish(client, client->entries + i, ETIMEDOUT);
|
||||
fast_multi_sock_client_finish(client,
|
||||
client->entries + i, ETIMEDOUT);
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"recv from %s:%d timedout",
|
||||
__LINE__, client->entries[i].conn->ip_addr,
|
||||
client->entries[i].conn->port);
|
||||
"recv from %s:%u timedout", __LINE__,
|
||||
format_ip_address(client->entries[i].conn->ip_addr,
|
||||
formatted_ip), client->entries[i].conn->port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -345,7 +415,8 @@ static int fast_multi_sock_client_deal_io(FastMultiSockClient *client)
|
|||
logInfo("file: "__FILE__", line: %d, pulling_count: %d, "
|
||||
"success_count: %d\n", __LINE__,
|
||||
client->pulling_count, client->success_count);
|
||||
return client->success_count > 0 ? 0 : (remain_timeout > 0 ? ENOENT : ETIMEDOUT);
|
||||
return client->success_count > 0 ? 0 :
|
||||
(remain_timeout > 0 ? ENOENT : ETIMEDOUT);
|
||||
}
|
||||
|
||||
int fast_multi_sock_client_request(FastMultiSockClient *client,
|
||||
|
|
@ -353,7 +424,8 @@ int fast_multi_sock_client_request(FastMultiSockClient *client,
|
|||
{
|
||||
int result;
|
||||
|
||||
client->deadline_time = get_current_time() + client->timeout;
|
||||
client->deadline_time_ms = client->get_current_time_ms_func() +
|
||||
client->timeout_ms;
|
||||
client->pulling_count = 0;
|
||||
client->success_count = 0;
|
||||
if ((result=fast_multi_sock_client_send_data(client, send_buffer)) != 0) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2018 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//multi_socket_client.h
|
||||
|
||||
|
|
@ -14,7 +21,6 @@
|
|||
#include <net/if.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include "common_define.h"
|
||||
#include "connection_pool.h"
|
||||
|
|
@ -22,22 +28,24 @@
|
|||
#include "ioevent.h"
|
||||
|
||||
typedef enum {
|
||||
fast_multi_sock_stage_recv_header = 'H',
|
||||
fast_multi_sock_stage_recv_body = 'B'
|
||||
fms_stage_recv_header = 'H',
|
||||
fms_stage_recv_body = 'B'
|
||||
} FastMultiSockRecvStage;
|
||||
|
||||
struct fast_multi_sock_client;
|
||||
struct fast_multi_sock_entry;
|
||||
|
||||
typedef int64_t (*fms_client_get_current_time_ms_func)();
|
||||
|
||||
//return the body length
|
||||
typedef int (*fast_multi_sock_client_get_body_length_func)(const FastBuffer *recv_buffer);
|
||||
typedef int (*fms_client_get_body_length_func)(const FastBuffer *recv_buffer);
|
||||
|
||||
//IO deal fucntion
|
||||
typedef int (*fast_multi_sock_client_io_func)(struct fast_multi_sock_client *client,
|
||||
struct fast_multi_sock_entry *entry);
|
||||
typedef int (*fast_multi_sock_client_io_func)(struct fast_multi_sock_client *
|
||||
client, struct fast_multi_sock_entry *entry);
|
||||
|
||||
typedef struct fast_multi_sock_entry {
|
||||
ConnectionInfo *conn; //the socket must be non-block socket
|
||||
ConnectionInfo *conn; //the connected socket must be non-block socket
|
||||
FastBuffer *send_buffer; //send buffer for internal use
|
||||
fast_multi_sock_client_io_func io_callback; //for internal use
|
||||
FastBuffer recv_buffer; //recv buffer for response package
|
||||
|
|
@ -52,10 +60,11 @@ typedef struct fast_multi_sock_client {
|
|||
int header_length; //package header size
|
||||
int pulling_count;
|
||||
int success_count;
|
||||
int timeout;
|
||||
time_t deadline_time;
|
||||
int timeout_ms;
|
||||
int64_t deadline_time_ms;
|
||||
FastMultiSockEntry *entries;
|
||||
fast_multi_sock_client_get_body_length_func get_body_length_func;
|
||||
fms_client_get_current_time_ms_func get_current_time_ms_func;
|
||||
fms_client_get_body_length_func get_body_length_func;
|
||||
IOEventPoller ioevent;
|
||||
} FastMultiSockClient;
|
||||
|
||||
|
|
@ -71,14 +80,33 @@ extern "C" {
|
|||
@param entry_count the count of socket entries
|
||||
@param header_length the header length of a package
|
||||
@param get_body_length_func the get body length function
|
||||
@param init_recv_buffer_size the initial size of response buffer
|
||||
@param get_current_time_ms_func the get current time in ms function
|
||||
@param init_recv_buffer_size the initial size of response buffer
|
||||
@param timeout_ms the timeout in milliseconds
|
||||
@return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_multi_sock_client_init_ex(FastMultiSockClient *client,
|
||||
FastMultiSockEntry *entries, const int entry_count,
|
||||
const int header_length,
|
||||
fms_client_get_body_length_func get_body_length_func,
|
||||
fms_client_get_current_time_ms_func get_current_time_ms_func,
|
||||
const int init_recv_buffer_size, const int timeout_ms);
|
||||
|
||||
/**
|
||||
init function
|
||||
@param client the client context
|
||||
@param entries the socket entries
|
||||
@param entry_count the count of socket entries
|
||||
@param header_length the header length of a package
|
||||
@param get_body_length_func the get body length function
|
||||
@param init_recv_buffer_size the initial size of response buffer
|
||||
@param timeout the timeout in seconds
|
||||
@return error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int fast_multi_sock_client_init(FastMultiSockClient *client,
|
||||
FastMultiSockEntry *entries, const int entry_count,
|
||||
const int header_length,
|
||||
fast_multi_sock_client_get_body_length_func get_body_length_func,
|
||||
fms_client_get_body_length_func get_body_length_func,
|
||||
const int init_recv_buffer_size, const int timeout);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* php7 extension wrapper
|
||||
* for compatibility, these wrapper functions are designed for old php version.
|
||||
*/
|
||||
|
|
@ -19,6 +34,22 @@
|
|||
#include <SAPI.h>
|
||||
#include <php_ini.h>
|
||||
|
||||
#ifndef TSRMLS_DC
|
||||
#define TSRMLS_DC
|
||||
#endif
|
||||
|
||||
#ifndef TSRMLS_C
|
||||
#define TSRMLS_C
|
||||
#endif
|
||||
|
||||
#ifndef TSRMLS_CC
|
||||
#define TSRMLS_CC
|
||||
#endif
|
||||
|
||||
#ifndef TSRMLS_FETCH
|
||||
#define TSRMLS_FETCH()
|
||||
#endif
|
||||
|
||||
#if PHP_MAJOR_VERSION < 7
|
||||
typedef int zend_size_t;
|
||||
#define ZEND_RETURN_STRING(s, dup) RETURN_STRING(s, dup)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -35,14 +50,14 @@ int write_to_pid_file(const char *pidFilename)
|
|||
char buff[32];
|
||||
int len;
|
||||
|
||||
len = sprintf(buff, "%d", (int)getpid());
|
||||
len = fc_itoa(getpid(), buff);
|
||||
return writeToFile(pidFilename, buff, len);
|
||||
}
|
||||
|
||||
int delete_pid_file(const char *pidFilename)
|
||||
{
|
||||
int result;
|
||||
pid_t pid;
|
||||
pid_t pid = 0;
|
||||
|
||||
if ((result=get_pid_from_file(pidFilename, &pid)) != 0) {
|
||||
return result;
|
||||
|
|
@ -97,41 +112,57 @@ static int do_stop(const char *pidFilename, const bool bShowError, pid_t *pid)
|
|||
}
|
||||
}
|
||||
|
||||
int process_stop(const char *pidFilename)
|
||||
int process_stop_ex(const char *pidFilename,
|
||||
const bool bShowError, bool *force)
|
||||
{
|
||||
pid_t pid;
|
||||
#define MAX_WAIT_COUNT 300
|
||||
pid_t pid = 0;
|
||||
int result;
|
||||
int sig;
|
||||
int i;
|
||||
|
||||
result = do_stop(pidFilename, true, &pid);
|
||||
if (result != 0) {
|
||||
*force = false;
|
||||
if ((result=do_stop(pidFilename, bShowError, &pid)) != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
fprintf(stderr, "waiting for pid [%d] exit ...\n", (int)pid);
|
||||
do {
|
||||
sleep(1);
|
||||
} while (kill(pid, SIGTERM) == 0);
|
||||
fprintf(stderr, "pid [%d] exit.\n", (int)pid);
|
||||
for (i=0; i<MAX_WAIT_COUNT; i++) {
|
||||
sig = (i % 10 == 0) ? SIGTERM : 0;
|
||||
if (kill(pid, sig) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
fc_sleep_ms(100);
|
||||
}
|
||||
|
||||
if (i == MAX_WAIT_COUNT) {
|
||||
if (kill(pid, SIGKILL) == 0) {
|
||||
fprintf(stderr, "waiting for pid [%d] exit timeout, "
|
||||
"force kill!\n", (int)pid);
|
||||
*force = true;
|
||||
fc_sleep_ms(100);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "pid [%d] exit.\n\n", (int)pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int process_restart(const char *pidFilename)
|
||||
{
|
||||
const bool bShowError = false;
|
||||
bool force;
|
||||
int result;
|
||||
pid_t pid;
|
||||
|
||||
result = do_stop(pidFilename, false, &pid);
|
||||
if (result == 0) {
|
||||
fprintf(stderr, "waiting for pid [%d] exit ...\n", (int)pid);
|
||||
do {
|
||||
sleep(1);
|
||||
} while (kill(pid, SIGTERM) == 0);
|
||||
fprintf(stderr, "starting ...\n");
|
||||
}
|
||||
|
||||
result = process_stop_ex(pidFilename, bShowError, &force);
|
||||
if (result == ENOENT || result == ESRCH) {
|
||||
return 0;
|
||||
result = 0;
|
||||
} else if (result == 0) {
|
||||
if (force) {
|
||||
sleep(1);
|
||||
}
|
||||
fprintf(stderr, "starting ...\n");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -168,7 +199,7 @@ static const char *get_exename_by_pid(const pid_t pid, char *buff,
|
|||
|
||||
int process_start(const char* pidFilename)
|
||||
{
|
||||
pid_t pid;
|
||||
pid_t pid = 0;
|
||||
int result;
|
||||
|
||||
if ((result=get_pid_from_file(pidFilename, &pid)) != 0) {
|
||||
|
|
@ -220,40 +251,41 @@ int process_start(const char* pidFilename)
|
|||
}
|
||||
}
|
||||
|
||||
int process_exist(const char *pidFilename)
|
||||
int process_exist(const char *pidFilename, pid_t *pid)
|
||||
{
|
||||
pid_t pid;
|
||||
int result;
|
||||
|
||||
if ((result=get_pid_from_file(pidFilename, &pid)) != 0) {
|
||||
if ((result=get_pid_from_file(pidFilename, pid)) != 0) {
|
||||
if (result == ENOENT) {
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "get pid from file: %s fail, " \
|
||||
"errno: %d, error info: %s\n",
|
||||
pidFilename, result, strerror(result));
|
||||
return true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (kill(pid, 0) == 0) {
|
||||
return true;
|
||||
if (kill(*pid, 0) == 0) {
|
||||
return 0;
|
||||
}
|
||||
else if (errno == ENOENT || errno == ESRCH) {
|
||||
return false;
|
||||
return ENOENT;
|
||||
}
|
||||
else {
|
||||
result = errno != 0 ? errno : EPERM;
|
||||
fprintf(stderr, "kill pid: %d fail, errno: %d, error info: %s\n",
|
||||
(int)pid, errno, strerror(errno));
|
||||
return true;
|
||||
(int)*pid, result, strerror(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
int get_base_path_from_conf_file(const char *filename, char *base_path,
|
||||
const int path_size)
|
||||
int get_base_path_from_conf_file_ex(const char *filename, char *base_path,
|
||||
const int path_size, const int noent_log_level)
|
||||
{
|
||||
char *pBasePath;
|
||||
string_t path_string;
|
||||
IniContext iniContext;
|
||||
int result;
|
||||
|
||||
|
|
@ -270,7 +302,7 @@ int get_base_path_from_conf_file(const char *filename, char *base_path,
|
|||
do
|
||||
{
|
||||
pBasePath = iniGetStrValue(NULL, "base_path", &iniContext);
|
||||
if (pBasePath == NULL)
|
||||
if (pBasePath == NULL || *pBasePath == '\0')
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"conf file \"%s\" must have item " \
|
||||
|
|
@ -279,16 +311,18 @@ int get_base_path_from_conf_file(const char *filename, char *base_path,
|
|||
break;
|
||||
}
|
||||
|
||||
snprintf(base_path, path_size, "%s", pBasePath);
|
||||
FC_SET_STRING(path_string, pBasePath);
|
||||
normalize_path(NULL, &path_string, base_path, path_size);
|
||||
chopPath(base_path);
|
||||
if (!fileExists(base_path))
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"\"%s\" can't be accessed, error info: %s", \
|
||||
__LINE__, base_path, STRERROR(errno));
|
||||
result = errno != 0 ? errno : ENOENT;
|
||||
break;
|
||||
}
|
||||
{
|
||||
result = errno != 0 ? errno : ENOENT;
|
||||
log_it_ex(&g_log_context, noent_log_level,
|
||||
"file: "__FILE__", line: %d, "
|
||||
"\"%s\" can't be accessed, error info: %s",
|
||||
__LINE__, base_path, STRERROR(result));
|
||||
break;
|
||||
}
|
||||
if (!isDir(base_path))
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
|
|
@ -305,6 +339,9 @@ int get_base_path_from_conf_file(const char *filename, char *base_path,
|
|||
|
||||
int process_action(const char *pidFilename, const char *action, bool *stop)
|
||||
{
|
||||
int result;
|
||||
pid_t pid;
|
||||
|
||||
*stop = false;
|
||||
if (action == NULL)
|
||||
{
|
||||
|
|
@ -316,6 +353,23 @@ int process_action(const char *pidFilename, const char *action, bool *stop)
|
|||
*stop = true;
|
||||
return process_stop(pidFilename);
|
||||
}
|
||||
else if (strcmp(action, "status") == 0)
|
||||
{
|
||||
*stop = true;
|
||||
result = process_exist(pidFilename, &pid);
|
||||
switch (result) {
|
||||
case 0:
|
||||
printf("Running, pid: %d\n", (int)pid);
|
||||
break;
|
||||
case ENOENT:
|
||||
printf("NOT running\n");
|
||||
break;
|
||||
default:
|
||||
printf("Unkown status\n");
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (strcmp(action, "restart") == 0)
|
||||
{
|
||||
return process_restart(pidFilename);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,18 @@
|
|||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PROCESS_CTRL_H
|
||||
#define PROCESS_CTRL_H
|
||||
|
|
@ -12,8 +27,11 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
int get_base_path_from_conf_file(const char *filename, char *base_path,
|
||||
const int path_size);
|
||||
int get_base_path_from_conf_file_ex(const char *filename, char *base_path,
|
||||
const int path_size, const int noent_log_level);
|
||||
|
||||
#define get_base_path_from_conf_file(filename, base_path, path_size) \
|
||||
get_base_path_from_conf_file_ex(filename, base_path, path_size, LOG_ERR)
|
||||
|
||||
int get_pid_from_file(const char *pidFilename, pid_t *pid);
|
||||
|
||||
|
|
@ -21,11 +39,19 @@ int write_to_pid_file(const char *pidFilename);
|
|||
|
||||
int delete_pid_file(const char *pidFilename);
|
||||
|
||||
int process_stop(const char *pidFilename);
|
||||
int process_stop_ex(const char *pidFilename,
|
||||
const bool bShowError, bool *force);
|
||||
|
||||
static inline int process_stop(const char *pidFilename)
|
||||
{
|
||||
const bool bShowError = true;
|
||||
bool force;
|
||||
return process_stop_ex(pidFilename, bShowError, &force);
|
||||
}
|
||||
|
||||
int process_restart(const char *pidFilename);
|
||||
|
||||
int process_exist(const char *pidFilename);
|
||||
int process_exist(const char *pidFilename, pid_t *pid);
|
||||
|
||||
int process_action(const char *pidFilename, const char *action, bool *stop);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -21,44 +28,42 @@
|
|||
#include <dirent.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include "pthread_func.h"
|
||||
#include "fc_memory.h"
|
||||
#include "logger.h"
|
||||
#include "pthread_func.h"
|
||||
|
||||
int init_pthread_lock(pthread_mutex_t *pthread_lock)
|
||||
{
|
||||
pthread_mutexattr_t mat;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_mutexattr_init(&mat)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutexattr_init fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
if ((result=pthread_mutexattr_init(&mat)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutexattr_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
if ((result=pthread_mutexattr_settype(&mat, \
|
||||
if ((result=pthread_mutexattr_settype(&mat,
|
||||
PTHREAD_MUTEX_ERRORCHECK)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutexattr_settype fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutexattr_settype fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
if ((result=pthread_mutex_init(pthread_lock, &mat)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_init fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
if ((result=pthread_mutex_init(pthread_lock, &mat)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_mutex_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
if ((result=pthread_mutexattr_destroy(&mat)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call thread_mutexattr_destroy fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
if ((result=pthread_mutexattr_destroy(&mat)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call thread_mutexattr_destroy fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
|
@ -66,52 +71,93 @@ int init_pthread_lock(pthread_mutex_t *pthread_lock)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int init_pthread_rwlock(pthread_rwlock_t *rwlock)
|
||||
{
|
||||
struct {
|
||||
pthread_rwlockattr_t holder;
|
||||
pthread_rwlockattr_t *ptr;
|
||||
} attr;
|
||||
int result;
|
||||
|
||||
|
||||
#ifdef WITH_PTHREAD_RWLOCKATTR_SETKIND_NP
|
||||
attr.ptr = &attr.holder;
|
||||
if ((result=pthread_rwlockattr_init(attr.ptr)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_rwlockattr_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result=pthread_rwlockattr_setkind_np(attr.ptr,
|
||||
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_rwlockattr_settype fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
attr.ptr = NULL;
|
||||
#endif
|
||||
|
||||
if ((result=pthread_rwlock_init(rwlock, attr.ptr)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_rwlock_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
if (attr.ptr != NULL) {
|
||||
if ((result=pthread_rwlockattr_destroy(attr.ptr)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call thread_rwlockattr_destroy fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_pthread_attr(pthread_attr_t *pattr, const int stack_size)
|
||||
{
|
||||
size_t old_stack_size;
|
||||
size_t new_stack_size;
|
||||
int result;
|
||||
|
||||
if ((result=pthread_attr_init(pattr)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_attr_init fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
if ((result=pthread_attr_init(pattr)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_attr_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result=pthread_attr_getstacksize(pattr, &old_stack_size)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_attr_getstacksize fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
if ((result=pthread_attr_getstacksize(pattr, &old_stack_size)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_attr_getstacksize fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (stack_size > 0)
|
||||
{
|
||||
if (old_stack_size != stack_size)
|
||||
{
|
||||
if (stack_size > 0) {
|
||||
if (old_stack_size != stack_size) {
|
||||
new_stack_size = stack_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
new_stack_size = 0;
|
||||
}
|
||||
}
|
||||
else if (old_stack_size < 1 * 1024 * 1024)
|
||||
{
|
||||
} else if (old_stack_size < 1 * 1024 * 1024) {
|
||||
new_stack_size = 1 * 1024 * 1024;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
new_stack_size = 0;
|
||||
}
|
||||
|
||||
if (new_stack_size > 0)
|
||||
{
|
||||
if (new_stack_size > 0) {
|
||||
if ((result=pthread_attr_setstacksize(pattr,
|
||||
new_stack_size)) != 0)
|
||||
{
|
||||
|
|
@ -123,12 +169,12 @@ int init_pthread_attr(pthread_attr_t *pattr, const int stack_size)
|
|||
}
|
||||
}
|
||||
|
||||
if ((result=pthread_attr_setdetachstate(pattr, \
|
||||
if ((result=pthread_attr_setdetachstate(pattr,
|
||||
PTHREAD_CREATE_DETACHED)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"call pthread_attr_setdetachstate fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"call pthread_attr_setdetachstate fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
|
@ -136,40 +182,101 @@ int init_pthread_attr(pthread_attr_t *pattr, const int stack_size)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int create_work_threads(int *count, void *(*start_func)(void *), \
|
||||
void *arg, pthread_t *tids, const int stack_size)
|
||||
int create_work_threads(int *count, void *(*start_func)(void *),
|
||||
void **args, pthread_t *tids, const int stack_size)
|
||||
{
|
||||
#define FIXED_TID_COUNT 256
|
||||
|
||||
int result;
|
||||
pthread_attr_t thread_attr;
|
||||
void **current_arg;
|
||||
pthread_t fixed_tids[FIXED_TID_COUNT];
|
||||
pthread_t *the_tids;
|
||||
pthread_t *ptid;
|
||||
pthread_t *ptid_end;
|
||||
|
||||
if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0)
|
||||
{
|
||||
if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (tids != NULL) {
|
||||
the_tids = tids;
|
||||
} else {
|
||||
if (*count <= FIXED_TID_COUNT) {
|
||||
the_tids = fixed_tids;
|
||||
} else {
|
||||
int bytes;
|
||||
bytes = sizeof(pthread_t) * *count;
|
||||
the_tids = (pthread_t *)fc_malloc(bytes);
|
||||
if (the_tids == NULL) {
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = 0;
|
||||
ptid_end = tids + (*count);
|
||||
for (ptid=tids; ptid<ptid_end; ptid++)
|
||||
{
|
||||
if ((result=pthread_create(ptid, &thread_attr, \
|
||||
start_func, arg)) != 0)
|
||||
ptid_end = the_tids + (*count);
|
||||
for (ptid=the_tids,current_arg=args; ptid<ptid_end;
|
||||
ptid++,current_arg++)
|
||||
{
|
||||
if ((result=pthread_create(ptid, &thread_attr,
|
||||
start_func, *current_arg)) != 0)
|
||||
{
|
||||
*count = ptid - tids;
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"create thread failed, startup threads: %d, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, *count, \
|
||||
*count = ptid - the_tids;
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"create threads #%d fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, *count,
|
||||
result, STRERROR(result));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (the_tids != tids && the_tids != fixed_tids) {
|
||||
free(the_tids);
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
return result;
|
||||
}
|
||||
|
||||
int create_work_threads_ex(int *count, void *(*start_func)(void *),
|
||||
void *args, const int elment_size, pthread_t *tids,
|
||||
const int stack_size)
|
||||
{
|
||||
#define FIXED_ARG_COUNT 256
|
||||
|
||||
void *fixed_args[FIXED_ARG_COUNT];
|
||||
void **pp_args;
|
||||
char *p;
|
||||
int result;
|
||||
int i;
|
||||
|
||||
if (*count <= FIXED_ARG_COUNT) {
|
||||
pp_args = fixed_args;
|
||||
} else {
|
||||
int bytes;
|
||||
bytes = sizeof(void *) * (*count);
|
||||
pp_args = (void **)fc_malloc(bytes);
|
||||
if (pp_args == NULL) {
|
||||
return ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
p = (char *)args;
|
||||
for (i=0; i<*count; i++) {
|
||||
pp_args[i] = p;
|
||||
p += elment_size;
|
||||
}
|
||||
result = create_work_threads(count, start_func,
|
||||
pp_args, tids, stack_size);
|
||||
if (pp_args != fixed_args) {
|
||||
free(pp_args);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int kill_work_threads(pthread_t *tids, const int count)
|
||||
{
|
||||
int result;
|
||||
|
|
@ -177,13 +284,11 @@ int kill_work_threads(pthread_t *tids, const int count)
|
|||
pthread_t *ptid_end;
|
||||
|
||||
ptid_end = tids + count;
|
||||
for (ptid=tids; ptid<ptid_end; ptid++)
|
||||
{
|
||||
if ((result=pthread_kill(*ptid, SIGINT)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"kill thread failed, " \
|
||||
"errno: %d, error info: %s", \
|
||||
for (ptid=tids; ptid<ptid_end; ptid++) {
|
||||
if ((result=pthread_kill(*ptid, SIGINT)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"kill thread failed, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
}
|
||||
|
|
@ -191,3 +296,53 @@ int kill_work_threads(pthread_t *tids, const int count)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int fc_create_thread(pthread_t *tid, void *(*start_func)(void *),
|
||||
void *args, const int stack_size)
|
||||
{
|
||||
int result;
|
||||
pthread_attr_t thread_attr;
|
||||
|
||||
if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result=pthread_create(tid, &thread_attr, start_func, args)) != 0) {
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"create thread fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
return result;
|
||||
}
|
||||
|
||||
int init_pthread_lock_cond_pair(pthread_lock_cond_pair_t *lcp)
|
||||
{
|
||||
int result;
|
||||
|
||||
if ((result=init_pthread_lock(&lcp->lock)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"init_pthread_lock fail, errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
if ((result=pthread_cond_init(&lcp->cond, NULL)) != 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"pthread_cond_init fail, "
|
||||
"errno: %d, error info: %s",
|
||||
__LINE__, result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void destroy_pthread_lock_cond_pair(pthread_lock_cond_pair_t *lcp)
|
||||
{
|
||||
pthread_cond_destroy(&lcp->cond);
|
||||
pthread_mutex_destroy(&lcp->lock);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PTHREAD_FUNC_H
|
||||
#define PTHREAD_FUNC_H
|
||||
|
|
@ -16,18 +23,184 @@
|
|||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include "common_define.h"
|
||||
#include "shared_func.h"
|
||||
#include "sched_thread.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int init_pthread_lock(pthread_mutex_t *pthread_lock);
|
||||
int init_pthread_rwlock(pthread_rwlock_t *rwlock);
|
||||
int init_pthread_attr(pthread_attr_t *pattr, const int stack_size);
|
||||
|
||||
int create_work_threads(int *count, void *(*start_func)(void *), \
|
||||
void *arg, pthread_t *tids, const int stack_size);
|
||||
int init_pthread_lock_cond_pair(pthread_lock_cond_pair_t *lcp);
|
||||
void destroy_pthread_lock_cond_pair(pthread_lock_cond_pair_t *lcp);
|
||||
|
||||
#define PTHREAD_MUTEX_LOCK(lock) \
|
||||
do { \
|
||||
int lock_res; \
|
||||
if ((lock_res=pthread_mutex_lock(lock)) != 0) \
|
||||
{ \
|
||||
logWarning("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_lock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, lock_res, STRERROR(lock_res)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define PTHREAD_MUTEX_UNLOCK(lock) \
|
||||
do { \
|
||||
int unlock_res; \
|
||||
if ((unlock_res=pthread_mutex_unlock(lock)) != 0) \
|
||||
{ \
|
||||
logWarning("file: "__FILE__", line: %d, " \
|
||||
"call pthread_mutex_unlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, unlock_res, STRERROR(unlock_res)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define PTHREAD_RWLOCK_WRLOCK(rwlock) \
|
||||
do { \
|
||||
int rwlock_res; \
|
||||
if ((rwlock_res=pthread_rwlock_wrlock(rwlock)) != 0) \
|
||||
{ \
|
||||
logWarning("file: "__FILE__", line: %d, " \
|
||||
"call pthread_rwlock_wrlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, rwlock_res, STRERROR(rwlock_res)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define PTHREAD_RWLOCK_RDLOCK(rwlock) \
|
||||
do { \
|
||||
int rwlock_res; \
|
||||
if ((rwlock_res=pthread_rwlock_rdlock(rwlock)) != 0) \
|
||||
{ \
|
||||
logWarning("file: "__FILE__", line: %d, " \
|
||||
"call pthread_rwlock_rdlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, rwlock_res, STRERROR(rwlock_res)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define PTHREAD_RWLOCK_UNLOCK(rwlock) \
|
||||
do { \
|
||||
int unlock_res; \
|
||||
if ((unlock_res=pthread_rwlock_unlock(rwlock)) != 0) \
|
||||
{ \
|
||||
logWarning("file: "__FILE__", line: %d, " \
|
||||
"call pthread_rwlock_unlock fail, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, unlock_res, STRERROR(unlock_res)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
#define lcp_timedwait_sec(lcp, timeout) \
|
||||
fc_timedwait_sec(&(lcp)->lock, &(lcp)->cond, timeout)
|
||||
|
||||
#define lcp_timedwait_ms(lcp, timeout_ms) \
|
||||
fc_timedwait_ms(&(lcp)->lock, &(lcp)->cond, timeout_ms)
|
||||
|
||||
static inline void fc_timedwait_sec(pthread_mutex_t *lock,
|
||||
pthread_cond_t *cond, const int timeout)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
ts.tv_sec = get_current_time() + timeout;
|
||||
ts.tv_nsec = 0;
|
||||
PTHREAD_MUTEX_LOCK(lock);
|
||||
pthread_cond_timedwait(cond, lock, &ts);
|
||||
PTHREAD_MUTEX_UNLOCK(lock);
|
||||
}
|
||||
|
||||
static inline void fc_timedwait_ms(pthread_mutex_t *lock,
|
||||
pthread_cond_t *cond, const int timeout_ms)
|
||||
{
|
||||
int64_t expires_ms;
|
||||
struct timespec ts;
|
||||
|
||||
expires_ms = get_current_time_ms() + timeout_ms;
|
||||
ts.tv_sec = expires_ms / 1000;
|
||||
ts.tv_nsec = (expires_ms % 1000) * (1000 * 1000);
|
||||
PTHREAD_MUTEX_LOCK(lock);
|
||||
pthread_cond_timedwait(cond, lock, &ts);
|
||||
PTHREAD_MUTEX_UNLOCK(lock);
|
||||
}
|
||||
|
||||
static inline int fc_timeout_to_timespec(const int timeout,
|
||||
const int time_unit, struct timespec *ts)
|
||||
{
|
||||
int seconds;
|
||||
|
||||
switch (time_unit) {
|
||||
case FC_TIME_UNIT_SECOND:
|
||||
seconds = timeout;
|
||||
ts->tv_nsec = 0;
|
||||
break;
|
||||
case FC_TIME_UNIT_MSECOND:
|
||||
seconds = timeout / 1000;
|
||||
ts->tv_nsec = (timeout % 1000) * (1000 * 1000);
|
||||
break;
|
||||
case FC_TIME_UNIT_USECOND:
|
||||
seconds = timeout / (1000 * 1000);
|
||||
ts->tv_nsec = (timeout % (1000 * 1000)) * 1000;
|
||||
break;
|
||||
case FC_TIME_UNIT_NSECOND:
|
||||
seconds = timeout / (1000 * 1000 * 1000);
|
||||
ts->tv_nsec = timeout % (1000 * 1000 * 1000);
|
||||
break;
|
||||
default:
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"invalid time unit: %d", __LINE__, time_unit);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ts->tv_sec = get_current_time() + seconds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int fc_cond_timedwait(pthread_lock_cond_pair_t *lcp,
|
||||
const int timeout, const int time_unit)
|
||||
{
|
||||
struct timespec ts;
|
||||
int result;
|
||||
|
||||
if ((result=fc_timeout_to_timespec(timeout, time_unit, &ts)) != 0) {
|
||||
return result;
|
||||
}
|
||||
return pthread_cond_timedwait(&lcp->cond, &lcp->lock, &ts);
|
||||
}
|
||||
|
||||
#define fc_cond_timedwait_sec(lcp, timeout) \
|
||||
fc_cond_timedwait(lcp, timeout, FC_TIME_UNIT_SECOND)
|
||||
|
||||
#define fc_cond_timedwait_ms(lcp, timeout_ms) \
|
||||
fc_cond_timedwait(lcp, timeout_ms, FC_TIME_UNIT_MSECOND)
|
||||
|
||||
#define fc_cond_timedwait_us(lcp, timeout_us) \
|
||||
fc_cond_timedwait(lcp, timeout_us, FC_TIME_UNIT_USECOND)
|
||||
|
||||
|
||||
int create_work_threads(int *count, void *(*start_func)(void *),
|
||||
void **args, pthread_t *tids, const int stack_size);
|
||||
|
||||
int create_work_threads_ex(int *count, void *(*start_func)(void *),
|
||||
void *args, const int elment_size, pthread_t *tids,
|
||||
const int stack_size);
|
||||
|
||||
int kill_work_threads(pthread_t *tids, const int count);
|
||||
|
||||
int fc_create_thread(pthread_t *tid, void *(*start_func)(void *),
|
||||
void *args, const int stack_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1,289 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com
|
||||
*
|
||||
* FastLib may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastLib source kit.
|
||||
* Please visit the FastLib Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pthread_pool.h"
|
||||
|
||||
/*
|
||||
*the thread pool
|
||||
*/
|
||||
// global varalibale declared
|
||||
static threadpool_info_t *pool;
|
||||
|
||||
/*
|
||||
* the thread callback function proxy
|
||||
* parameters:
|
||||
* arg:the thread callback function parameter
|
||||
*/
|
||||
static void *callback_proxy(void *arg);
|
||||
|
||||
/*
|
||||
* push the thread into the pool
|
||||
* parameters:
|
||||
* thread:the thread will push into the poolbool
|
||||
* return:
|
||||
* 0:success
|
||||
* >0 : fail
|
||||
*/
|
||||
static int push2pool(thread_info_t *thread);
|
||||
|
||||
// proxy the thread running, use pthread_cond_wait to wait for arg to be update by
|
||||
// other users that need to use this thread
|
||||
static void *callback_proxy(void *arg)
|
||||
{
|
||||
thread_info_t* thread = (thread_info_t *) arg;
|
||||
// runs only when the pool->state is initialized
|
||||
while(initialized == pool->state)
|
||||
{
|
||||
// run what the caller want to do
|
||||
thread->func(thread->arg);
|
||||
|
||||
// if the state of thread pool is changed
|
||||
// we termiate the execution of this thread by returning the result
|
||||
if(pool == NULL || initialized != pool->state) break;
|
||||
|
||||
pthread_mutex_lock(&thread->mutex_locker);
|
||||
|
||||
if(0 == push2pool(thread))
|
||||
{
|
||||
pthread_cond_wait(&thread->run_locker,&thread->mutex_locker);
|
||||
pthread_mutex_unlock(&thread->mutex_locker);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_unlock( &thread->mutex_locker );
|
||||
pthread_cond_destroy( &thread->run_locker );
|
||||
pthread_mutex_destroy( &thread->mutex_locker );
|
||||
|
||||
free( thread );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&pool->mutex_locker);
|
||||
pool->current_size --;
|
||||
if(0 >= pool->current_size) pthread_cond_signal(&pool->empty_locker);
|
||||
pthread_mutex_unlock(&pool->mutex_locker);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// push the free thread_info_t back to the thread pool
|
||||
static int push2pool(thread_info_t *thread)
|
||||
{
|
||||
int result = -1;
|
||||
do
|
||||
{
|
||||
pthread_mutex_lock(&pool->mutex_locker);
|
||||
if( pool->current_index < pool->total_size )
|
||||
{
|
||||
pool->list[ pool->current_index ] = thread;
|
||||
pool->current_index++;
|
||||
result = 0;
|
||||
|
||||
// there is new thread for use, call phtread_cond_signal to
|
||||
// notice the caller
|
||||
pthread_cond_signal( &pool->run_locker);
|
||||
|
||||
if( pool->current_index >= pool->current_size )
|
||||
{
|
||||
// current_index reach the max
|
||||
// notice other thread that I am full
|
||||
pthread_cond_signal( &pool->full_locker );
|
||||
}
|
||||
}
|
||||
}while(0);
|
||||
pthread_mutex_unlock(&pool->mutex_locker);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// initialize a thread pool of [size] for later use
|
||||
int threadpool_init(int size)
|
||||
{
|
||||
if(0 >= size)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
pool = (threadpool_info_t *) malloc(sizeof(threadpool_info_t));
|
||||
if(NULL == pool)
|
||||
{
|
||||
return -2;
|
||||
}
|
||||
memset(pool,0,sizeof(threadpool_info_t));
|
||||
pool->state = initializing;
|
||||
pool->total_size = size;
|
||||
pool->current_size = 0;
|
||||
pool->current_index = 0;
|
||||
// initialize sync data structures
|
||||
pthread_mutex_init(&pool->mutex_locker,NULL);
|
||||
pthread_cond_init(&pool->run_locker,NULL);
|
||||
pthread_cond_init(&pool->empty_locker,NULL);
|
||||
pthread_cond_init(&pool->full_locker,NULL);
|
||||
// initialize a list of thread_info_t structs
|
||||
pool->list = (thread_info_t **) malloc(sizeof(thread_info_t*) * size);
|
||||
if(NULL == pool->list)
|
||||
{
|
||||
pthread_cond_destroy(&pool->run_locker);
|
||||
pthread_cond_destroy(&pool->empty_locker);
|
||||
pthread_cond_destroy(&pool->full_locker);
|
||||
pthread_mutex_destroy(&pool->mutex_locker);
|
||||
// free the memory pointed by pool pointer
|
||||
free(pool);
|
||||
return -2;
|
||||
}
|
||||
|
||||
pool->state = initialized;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// run the callback within the thread pool, arg is its var
|
||||
int threadpool_run(callback func,void *arg)
|
||||
{
|
||||
if(NULL == pool)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
do
|
||||
{
|
||||
pthread_mutex_lock(&pool->mutex_locker);
|
||||
if(NULL == pool || initialized != pool->state) //the pool cannot use
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
//current size is >= the max pool size and all thread are busy now
|
||||
while(pool->current_index <= 0 && pool->current_size >= pool->total_size)
|
||||
{
|
||||
// wait on the run locker when there is no spare thread for the caller to use
|
||||
pthread_cond_wait(&pool->run_locker,&pool->mutex_locker);
|
||||
}
|
||||
|
||||
if(0 >= pool->current_index)
|
||||
{
|
||||
// when the current_index is smaler or equal to 0
|
||||
// we create a new thread_info_t data structure and put it into the pool later
|
||||
thread_info_t * thread = (thread_info_t *) malloc(sizeof(thread_info_t));
|
||||
if(NULL == thread)
|
||||
{
|
||||
result = -2;
|
||||
break;
|
||||
}
|
||||
memset(thread,0,sizeof(thread_info_t));
|
||||
|
||||
// create thread and set it to detached mode(it will end by itself when it finishes)
|
||||
pthread_mutex_init(&thread->mutex_locker,NULL);
|
||||
pthread_cond_init(&thread->run_locker,NULL);
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
|
||||
|
||||
thread->arg = arg;
|
||||
thread->func = func;
|
||||
|
||||
if(0 == pthread_create(&thread->id,&attr,callback_proxy,thread))
|
||||
{
|
||||
pool->current_size ++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = -3;
|
||||
pthread_mutex_destroy(&thread->mutex_locker);
|
||||
pthread_cond_destroy(&thread->run_locker);
|
||||
free(thread);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
pool->current_index --;//because the array begin with 0
|
||||
thread_info_t *thread = pool->list[ pool->current_index ];
|
||||
pool->list[ pool->current_index ] = NULL;
|
||||
|
||||
thread->func = func;
|
||||
thread->arg = arg;
|
||||
|
||||
pthread_mutex_lock( &thread->mutex_locker );
|
||||
pthread_cond_signal( &thread->run_locker ) ;
|
||||
pthread_mutex_unlock ( &thread->mutex_locker );
|
||||
}
|
||||
}while(0);
|
||||
pthread_mutex_unlock(&pool->mutex_locker);
|
||||
return result;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// destory the thread pool
|
||||
int threadpool_destroy()
|
||||
{
|
||||
if(NULL == pool) return 0;
|
||||
|
||||
pthread_mutex_lock( &pool->mutex_locker);
|
||||
|
||||
// when current_index is biger or equals to current_size
|
||||
// this means all the job in the thread pool is finished
|
||||
// which means we can do the free related jobs
|
||||
if( pool->current_index < pool->current_size )
|
||||
{
|
||||
// if current_index < current_size, wait for current_index to reach current_size
|
||||
// then change the state of the pool, thus stopping the caller to put more task
|
||||
// into the thread pool, then it use pthread_cond_signal to cause all worker thread
|
||||
// to exit.
|
||||
pthread_cond_wait( &pool->full_locker, &pool->mutex_locker );
|
||||
}
|
||||
|
||||
pool->state = uninstalling;
|
||||
int i = 0;
|
||||
|
||||
// cause all the thread to run to its end
|
||||
for( i = 0; i < pool->current_index; i++ )
|
||||
{
|
||||
thread_info_t *thread = pool->list[i];
|
||||
|
||||
pthread_mutex_lock( &thread->mutex_locker );
|
||||
pthread_cond_signal( &thread->run_locker ) ;
|
||||
pthread_mutex_unlock ( &thread->mutex_locker );
|
||||
}
|
||||
|
||||
// wait for all threads to exit, when all threads exit, it will
|
||||
// issue a signal of empty_locker
|
||||
if(0 < pool->current_size)
|
||||
{
|
||||
pthread_cond_wait( &pool->empty_locker, &pool->mutex_locker);
|
||||
}
|
||||
|
||||
for( i = 0; i < pool->current_index; i++ )
|
||||
{
|
||||
free( pool->list[ i ] );
|
||||
pool->list[ i ] = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock( &pool->mutex_locker );
|
||||
|
||||
pool->current_index = 0;
|
||||
|
||||
pthread_mutex_destroy( &pool->mutex_locker );
|
||||
pthread_cond_destroy( &pool->run_locker );
|
||||
pthread_cond_destroy( &pool->full_locker );
|
||||
pthread_cond_destroy( &pool->empty_locker );
|
||||
|
||||
free( pool->list );
|
||||
pool->list = NULL;
|
||||
free( pool);
|
||||
pool = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Seapeak.Xu / xvhfeng@gmail.com
|
||||
*
|
||||
* FastLib may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastLib source kit.
|
||||
* Please visit the FastLib Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
|
||||
#ifndef PTHREAD_POOL_H_
|
||||
#define PTHREAD_POOL_H_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/*
|
||||
* define the callback function type of thread
|
||||
*/
|
||||
typedef void (*callback)(void *);
|
||||
|
||||
|
||||
/*
|
||||
* the thread pool state
|
||||
* member:
|
||||
* uninitialized : not initialize the thread pool.
|
||||
* initializing : initializing the thread pool.
|
||||
* initialized : the pool can use.
|
||||
* uninstalling : uninstalling the thread pool.
|
||||
* uninstalled : uninstall the thread pool is over.
|
||||
*/
|
||||
typedef enum threadpool_state
|
||||
{
|
||||
uninitialized,
|
||||
initializing,
|
||||
initialized,
|
||||
uninstalling,
|
||||
uninstalled,
|
||||
}thread_state_t;
|
||||
|
||||
|
||||
/*
|
||||
* define the thread type which in the pool
|
||||
* members:
|
||||
* id : the thread id
|
||||
* mutex_locker : the mutext locker
|
||||
* run_locker : the locker for noticing the thread do running or waitting
|
||||
* func : the callback function for thread
|
||||
* arg : the callback parameter
|
||||
*/
|
||||
typedef struct thread_info
|
||||
{
|
||||
pthread_t id;
|
||||
pthread_mutex_t mutex_locker;
|
||||
pthread_cond_t run_locker;
|
||||
callback func;
|
||||
void *arg;
|
||||
}thread_info_t;
|
||||
|
||||
/*
|
||||
* the structure for the thread pool
|
||||
* member:
|
||||
* list : the initialazed thread list
|
||||
* mutex_locker : the mutex locker for the thread operation.
|
||||
* run_locker : the locker for noticing the thread do running or waitting.
|
||||
* full_locker : the locker notice the thread is stoping when free the thread pool and the pool is not full .
|
||||
* empty_Locker : the locker notice the thread waitting for the busy thread work over,then do with the thread.
|
||||
* state : the pool's current state.
|
||||
* total_size : the pool max size;
|
||||
* current_size : the thread count for the current pool ;
|
||||
* current_index : the busy thread in the pool index.
|
||||
*/
|
||||
typedef struct threadpool_info
|
||||
{
|
||||
thread_info_t **list;
|
||||
pthread_mutex_t mutex_locker;
|
||||
pthread_cond_t run_locker;
|
||||
pthread_cond_t full_locker;
|
||||
pthread_cond_t empty_locker;
|
||||
thread_state_t state;
|
||||
int total_size;
|
||||
int current_size;
|
||||
int current_index;
|
||||
}threadpool_info_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* initialize the thread pool
|
||||
* parameters:
|
||||
* size : thread pool max size
|
||||
* return:
|
||||
* 0:initialize pool success;
|
||||
* -1:the size parameter is less 0;
|
||||
* -2:initialize pool is fail,malloc memory for pool or pool->list is error;
|
||||
*/
|
||||
int threadpool_init(int size);
|
||||
|
||||
/*
|
||||
* run the function with the thread from pool
|
||||
* parameter:
|
||||
* func:the thread callback function
|
||||
* arg:the parameter of callback function
|
||||
* return:
|
||||
* 0 : success
|
||||
* -1: the pool is NULL;
|
||||
* -2 : malloc memory for thread is error;
|
||||
* -3 : create thread is error;
|
||||
*/
|
||||
int threadpool_run(callback func,void *arg);
|
||||
|
||||
/*
|
||||
* free and destroy the thread pool memory
|
||||
* return:
|
||||
* 0 : success
|
||||
* less 0 : fail
|
||||
*/
|
||||
int threadpool_destroy();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* PTHREAD_POOL_H_ */
|
||||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -13,9 +20,10 @@
|
|||
#include "shared_func.h"
|
||||
#include "pthread_func.h"
|
||||
#include "logger.h"
|
||||
#include "fc_memory.h"
|
||||
#include "sched_thread.h"
|
||||
|
||||
volatile bool g_schedule_flag = false;
|
||||
volatile int g_schedule_flag = false;
|
||||
volatile time_t g_current_time = 0;
|
||||
|
||||
static ScheduleArray waiting_schedule_array = {NULL, 0};
|
||||
|
|
@ -33,36 +41,84 @@ static int sched_dup_array(const ScheduleArray *pSrcArray,
|
|||
|
||||
static int sched_cmp_by_next_call_time(const void *p1, const void *p2)
|
||||
{
|
||||
return ((ScheduleEntry *)p1)->next_call_time - \
|
||||
return ((ScheduleEntry *)p1)->next_call_time -
|
||||
((ScheduleEntry *)p2)->next_call_time;
|
||||
}
|
||||
|
||||
static int sched_init_entries(ScheduleArray *pScheduleArray)
|
||||
time_t sched_make_first_call_time(struct tm *tm_current,
|
||||
const TimeInfo *time_base, const int interval)
|
||||
{
|
||||
int remain;
|
||||
struct {
|
||||
time_t time;
|
||||
struct tm tm;
|
||||
} base;
|
||||
|
||||
if (time_base->hour == TIME_NONE)
|
||||
{
|
||||
return g_current_time + interval;
|
||||
}
|
||||
|
||||
if (tm_current->tm_hour > time_base->hour ||
|
||||
(tm_current->tm_hour == time_base->hour
|
||||
&& tm_current->tm_min >= time_base->minute))
|
||||
{
|
||||
base.tm = *tm_current;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.time = g_current_time - 24 * 3600;
|
||||
localtime_r(&base.time, &base.tm);
|
||||
}
|
||||
|
||||
base.tm.tm_hour = time_base->hour;
|
||||
base.tm.tm_min = time_base->minute;
|
||||
if (time_base->second >= 0 && time_base->second <= 59)
|
||||
{
|
||||
base.tm.tm_sec = time_base->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
base.tm.tm_sec = 0;
|
||||
}
|
||||
base.time = mktime(&base.tm);
|
||||
remain = g_current_time - base.time;
|
||||
if (remain > 0)
|
||||
{
|
||||
return g_current_time + interval - remain % interval;
|
||||
}
|
||||
else if (remain < 0)
|
||||
{
|
||||
return g_current_time + (-1 * remain) % interval;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_current_time;
|
||||
}
|
||||
}
|
||||
|
||||
static int sched_init_entries(ScheduleEntry *entries, const int count)
|
||||
{
|
||||
ScheduleEntry *pEntry;
|
||||
ScheduleEntry *pEnd;
|
||||
time_t time_base;
|
||||
struct tm tm_current;
|
||||
struct tm tm_base;
|
||||
int remain;
|
||||
int interval;
|
||||
|
||||
if (pScheduleArray->count < 0)
|
||||
if (count < 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"schedule count %d < 0", \
|
||||
__LINE__, pScheduleArray->count);
|
||||
__LINE__, count);
|
||||
return EINVAL;
|
||||
}
|
||||
if (pScheduleArray->count == 0)
|
||||
if (count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_current_time = time(NULL);
|
||||
localtime_r((time_t *)&g_current_time, &tm_current);
|
||||
pEnd = pScheduleArray->entries + pScheduleArray->count;
|
||||
for (pEntry=pScheduleArray->entries; pEntry<pEnd; pEntry++)
|
||||
pEnd = entries + count;
|
||||
for (pEntry=entries; pEntry<pEnd; pEntry++)
|
||||
{
|
||||
if (next_id < pEntry->id)
|
||||
{
|
||||
|
|
@ -71,70 +127,26 @@ static int sched_init_entries(ScheduleArray *pScheduleArray)
|
|||
|
||||
if (pEntry->interval <= 0)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"shedule interval %d <= 0", \
|
||||
__LINE__, pEntry->interval);
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
"shedule id: %d, interval %d <= 0",
|
||||
__LINE__, pEntry->id, pEntry->interval);
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (pEntry->time_base.hour == TIME_NONE)
|
||||
{
|
||||
pEntry->next_call_time = g_current_time + \
|
||||
pEntry->interval;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tm_current.tm_hour > pEntry->time_base.hour || \
|
||||
(tm_current.tm_hour == pEntry->time_base.hour \
|
||||
&& tm_current.tm_min >= pEntry->time_base.minute))
|
||||
{
|
||||
memcpy(&tm_base, &tm_current, sizeof(struct tm));
|
||||
}
|
||||
else
|
||||
{
|
||||
time_base = g_current_time - 24 * 3600;
|
||||
localtime_r(&time_base, &tm_base);
|
||||
}
|
||||
pEntry->next_call_time = sched_make_first_call_time(
|
||||
&tm_current, &pEntry->time_base, pEntry->interval);
|
||||
|
||||
tm_base.tm_hour = pEntry->time_base.hour;
|
||||
tm_base.tm_min = pEntry->time_base.minute;
|
||||
if (pEntry->time_base.second >= 0 && pEntry->time_base.second <= 59)
|
||||
{
|
||||
tm_base.tm_sec = pEntry->time_base.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
tm_base.tm_sec = 0;
|
||||
}
|
||||
time_base = mktime(&tm_base);
|
||||
remain = g_current_time - time_base;
|
||||
if (remain > 0)
|
||||
{
|
||||
interval = pEntry->interval - remain % pEntry->interval;
|
||||
}
|
||||
else if (remain < 0)
|
||||
{
|
||||
interval = (-1 * remain) % pEntry->interval;
|
||||
}
|
||||
else
|
||||
{
|
||||
interval = 0;
|
||||
}
|
||||
|
||||
pEntry->next_call_time = g_current_time + interval;
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
{
|
||||
char buff1[32];
|
||||
char buff2[32];
|
||||
logInfo("id=%d, current time=%s, first call time=%s\n", \
|
||||
pEntry->id, formatDatetime(g_current_time, \
|
||||
"%Y-%m-%d %H:%M:%S", buff1, sizeof(buff1)), \
|
||||
formatDatetime(pEntry->next_call_time, \
|
||||
logInfo("id=%d, current time=%s, first call time=%s",
|
||||
pEntry->id, formatDatetime(g_current_time,
|
||||
"%Y-%m-%d %H:%M:%S", buff1, sizeof(buff1)),
|
||||
formatDatetime(pEntry->next_call_time,
|
||||
"%Y-%m-%d %H:%M:%S", buff2, sizeof(buff2)));
|
||||
}
|
||||
*/
|
||||
*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -153,7 +165,7 @@ static void sched_make_chain(ScheduleContext *pContext)
|
|||
return;
|
||||
}
|
||||
|
||||
qsort(pScheduleArray->entries, pScheduleArray->count, \
|
||||
qsort(pScheduleArray->entries, pScheduleArray->count,
|
||||
sizeof(ScheduleEntry), sched_cmp_by_next_call_time);
|
||||
|
||||
pContext->head = pScheduleArray->entries;
|
||||
|
|
@ -210,10 +222,11 @@ static int print_all_sched_entries(ScheduleArray *pScheduleArray)
|
|||
pEntry->time_base.minute, pEntry->time_base.second);
|
||||
}
|
||||
logInfo("id: %u, time_base: %s, interval: %d, "
|
||||
"new_thread: %s, task_func: %p, args: %p",
|
||||
pEntry->id, timebase, pEntry->interval,
|
||||
pEntry->new_thread ? "true" : "false",
|
||||
pEntry->task_func, pEntry->func_args);
|
||||
"new_thread: %s, task_func: %p, args: %p, "
|
||||
"next_call_time: %d", pEntry->id, timebase,
|
||||
pEntry->interval, pEntry->new_thread ? "true" : "false",
|
||||
pEntry->task_func, pEntry->func_args,
|
||||
(int)pEntry->next_call_time);
|
||||
}
|
||||
|
||||
free(sortedByIdArray.entries);
|
||||
|
|
@ -223,6 +236,7 @@ static int print_all_sched_entries(ScheduleArray *pScheduleArray)
|
|||
static int do_check_waiting(ScheduleContext *pContext)
|
||||
{
|
||||
ScheduleArray *pScheduleArray;
|
||||
ScheduleEntry *waitingEntries;
|
||||
ScheduleEntry *newEntries;
|
||||
ScheduleEntry *pWaitingEntry;
|
||||
ScheduleEntry *pWaitingEnd;
|
||||
|
|
@ -230,8 +244,8 @@ static int do_check_waiting(ScheduleContext *pContext)
|
|||
ScheduleEntry *pSchedEnd;
|
||||
int allocCount;
|
||||
int newCount;
|
||||
int result;
|
||||
int deleteCount;
|
||||
int waitingCount;
|
||||
|
||||
pScheduleArray = &(pContext->scheduleArray);
|
||||
deleteCount = 0;
|
||||
|
|
@ -269,7 +283,17 @@ static int do_check_waiting(ScheduleContext *pContext)
|
|||
waiting_del_id = -1;
|
||||
}
|
||||
|
||||
if (waiting_schedule_array.count == 0)
|
||||
PTHREAD_MUTEX_LOCK(&schedule_context->lock);
|
||||
waitingCount = waiting_schedule_array.count;
|
||||
waitingEntries = waiting_schedule_array.entries;
|
||||
if (waiting_schedule_array.entries != NULL)
|
||||
{
|
||||
waiting_schedule_array.count = 0;
|
||||
waiting_schedule_array.entries = NULL;
|
||||
}
|
||||
PTHREAD_MUTEX_UNLOCK(&schedule_context->lock);
|
||||
|
||||
if (waitingCount == 0)
|
||||
{
|
||||
if (deleteCount > 0)
|
||||
{
|
||||
|
|
@ -280,50 +304,41 @@ static int do_check_waiting(ScheduleContext *pContext)
|
|||
return ENOENT;
|
||||
}
|
||||
|
||||
allocCount = pScheduleArray->count + waiting_schedule_array.count;
|
||||
newEntries = (ScheduleEntry *)malloc(sizeof(ScheduleEntry) * allocCount);
|
||||
allocCount = pScheduleArray->count + waitingCount;
|
||||
newEntries = (ScheduleEntry *)fc_malloc(sizeof(ScheduleEntry) * allocCount);
|
||||
if (newEntries == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes failed, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, (int)sizeof(ScheduleEntry) * allocCount, \
|
||||
result, STRERROR(result));
|
||||
|
||||
if (deleteCount > 0)
|
||||
{
|
||||
sched_make_chain(pContext);
|
||||
}
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (pScheduleArray->count > 0)
|
||||
{
|
||||
memcpy(newEntries, pScheduleArray->entries, \
|
||||
memcpy(newEntries, pScheduleArray->entries,
|
||||
sizeof(ScheduleEntry) * pScheduleArray->count);
|
||||
}
|
||||
newCount = pScheduleArray->count;
|
||||
pWaitingEnd = waiting_schedule_array.entries + waiting_schedule_array.count;
|
||||
for (pWaitingEntry=waiting_schedule_array.entries; \
|
||||
pWaitingEntry<pWaitingEnd; pWaitingEntry++)
|
||||
pWaitingEnd = waitingEntries + waitingCount;
|
||||
for (pWaitingEntry=waitingEntries; pWaitingEntry<pWaitingEnd;
|
||||
pWaitingEntry++)
|
||||
{
|
||||
pSchedEnd = newEntries + newCount;
|
||||
for (pSchedEntry=newEntries; pSchedEntry<pSchedEnd; \
|
||||
pSchedEntry++)
|
||||
{
|
||||
if (pWaitingEntry->id == pSchedEntry->id)
|
||||
{
|
||||
memcpy(pSchedEntry, pWaitingEntry, \
|
||||
sizeof(ScheduleEntry));
|
||||
break;
|
||||
}
|
||||
{
|
||||
*pSchedEntry = *pWaitingEntry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pSchedEntry == pSchedEnd)
|
||||
{
|
||||
memcpy(pSchedEntry, pWaitingEntry, \
|
||||
sizeof(ScheduleEntry));
|
||||
*pSchedEntry = *pWaitingEntry;
|
||||
newCount++;
|
||||
}
|
||||
}
|
||||
|
|
@ -331,7 +346,7 @@ static int do_check_waiting(ScheduleContext *pContext)
|
|||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"schedule add entries: %d, replace entries: %d",
|
||||
__LINE__, newCount - pScheduleArray->count, \
|
||||
waiting_schedule_array.count - (newCount - pScheduleArray->count));
|
||||
waitingCount - (newCount - pScheduleArray->count));
|
||||
|
||||
if (pScheduleArray->entries != NULL)
|
||||
{
|
||||
|
|
@ -339,13 +354,9 @@ static int do_check_waiting(ScheduleContext *pContext)
|
|||
}
|
||||
pScheduleArray->entries = newEntries;
|
||||
pScheduleArray->count = newCount;
|
||||
|
||||
free(waiting_schedule_array.entries);
|
||||
waiting_schedule_array.count = 0;
|
||||
waiting_schedule_array.entries = NULL;
|
||||
free(waitingEntries);
|
||||
|
||||
sched_make_chain(pContext);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -369,6 +380,10 @@ static void *sched_call_func(void *args)
|
|||
TaskFunc task_func;
|
||||
int task_id;
|
||||
|
||||
#ifdef OS_LINUX
|
||||
prctl(PR_SET_NAME, "sched-call");
|
||||
#endif
|
||||
|
||||
pEntry = (ScheduleEntry *)args;
|
||||
task_func = pEntry->task_func;
|
||||
func_args = pEntry->func_args;
|
||||
|
|
@ -397,15 +412,20 @@ static void *sched_thread_entrance(void *args)
|
|||
int exec_count;
|
||||
int i;
|
||||
|
||||
#ifdef OS_LINUX
|
||||
prctl(PR_SET_NAME, "sched");
|
||||
#endif
|
||||
|
||||
pContext = (ScheduleContext *)args;
|
||||
if (sched_init_entries(&(pContext->scheduleArray)) != 0)
|
||||
if (sched_init_entries(pContext->scheduleArray.entries,
|
||||
pContext->scheduleArray.count) != 0)
|
||||
{
|
||||
free(pContext);
|
||||
return NULL;
|
||||
}
|
||||
sched_make_chain(pContext);
|
||||
|
||||
g_schedule_flag = true;
|
||||
__sync_bool_compare_and_swap(&g_schedule_flag, 0, 1);
|
||||
while (*(pContext->pcontinue_flag))
|
||||
{
|
||||
g_current_time = time(NULL);
|
||||
|
|
@ -418,6 +438,11 @@ static void *sched_thread_entrance(void *args)
|
|||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
logInfo("task count: %d, next_call_time: %d, g_current_time: %d",
|
||||
pContext->scheduleArray.count,
|
||||
(int)pContext->head->next_call_time, (int)g_current_time);
|
||||
*/
|
||||
while (pContext->head->next_call_time > g_current_time &&
|
||||
*(pContext->pcontinue_flag))
|
||||
{
|
||||
|
|
@ -438,7 +463,7 @@ static void *sched_thread_entrance(void *args)
|
|||
|
||||
exec_count = 0;
|
||||
pCurrent = pContext->head;
|
||||
while (*(pContext->pcontinue_flag) && (pCurrent != NULL \
|
||||
while (*(pContext->pcontinue_flag) && (pCurrent != NULL
|
||||
&& pCurrent->next_call_time <= g_current_time))
|
||||
{
|
||||
//logInfo("exec task id: %d", pCurrent->id);
|
||||
|
|
@ -462,13 +487,13 @@ static void *sched_thread_entrance(void *args)
|
|||
}
|
||||
else
|
||||
{
|
||||
usleep(1*1000);
|
||||
fc_sleep_ms(1);
|
||||
for (i=1; !pCurrent->thread_running && i<100; i++)
|
||||
{
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"task_id: %d, waiting thread ready, count %d",
|
||||
__LINE__, pCurrent->id, i);
|
||||
usleep(1*1000);
|
||||
fc_sleep_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -529,7 +554,7 @@ static void *sched_thread_entrance(void *args)
|
|||
}
|
||||
}
|
||||
|
||||
g_schedule_flag = false;
|
||||
__sync_bool_compare_and_swap(&g_schedule_flag, 1, 0);
|
||||
|
||||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"schedule thread exit", __LINE__);
|
||||
|
|
@ -541,7 +566,6 @@ static void *sched_thread_entrance(void *args)
|
|||
static int sched_dup_array(const ScheduleArray *pSrcArray, \
|
||||
ScheduleArray *pDestArray)
|
||||
{
|
||||
int result;
|
||||
int bytes;
|
||||
|
||||
if (pSrcArray->count == 0)
|
||||
|
|
@ -552,15 +576,10 @@ static int sched_dup_array(const ScheduleArray *pSrcArray, \
|
|||
}
|
||||
|
||||
bytes = sizeof(ScheduleEntry) * pSrcArray->count;
|
||||
pDestArray->entries = (ScheduleEntry *)malloc(bytes);
|
||||
pDestArray->entries = (ScheduleEntry *)fc_malloc(bytes);
|
||||
if (pDestArray->entries == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes failed, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, bytes, result, STRERROR(result));
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(pDestArray->entries, pSrcArray->entries, bytes);
|
||||
|
|
@ -568,28 +587,17 @@ static int sched_dup_array(const ScheduleArray *pSrcArray, \
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sched_append_array(const ScheduleArray *pSrcArray, \
|
||||
static int sched_append_array(const ScheduleArray *pSrcArray,
|
||||
ScheduleArray *pDestArray)
|
||||
{
|
||||
int result;
|
||||
int bytes;
|
||||
ScheduleEntry *new_entries;
|
||||
|
||||
if (pSrcArray->count == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes = sizeof(ScheduleEntry) * (pDestArray->count + pSrcArray->count);
|
||||
new_entries = (ScheduleEntry *)malloc(bytes);
|
||||
new_entries = (ScheduleEntry *)fc_malloc(bytes);
|
||||
if (new_entries == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes failed, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, bytes, result, STRERROR(result));
|
||||
return result;
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
if (pDestArray->entries != NULL)
|
||||
|
|
@ -606,37 +614,66 @@ static int sched_append_array(const ScheduleArray *pSrcArray, \
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sched_thread_init_ex(ScheduleContext **ppContext)
|
||||
{
|
||||
int result;
|
||||
|
||||
*ppContext = (ScheduleContext *)fc_malloc(sizeof(ScheduleContext));
|
||||
if (*ppContext == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
}
|
||||
memset(*ppContext, 0, sizeof(ScheduleContext));
|
||||
|
||||
if ((result=init_pthread_lock(&(*ppContext)->lock)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sched_add_entries(const ScheduleArray *pScheduleArray)
|
||||
{
|
||||
int result;
|
||||
ScheduleEntry *newStart;
|
||||
int old_count;
|
||||
int result;
|
||||
|
||||
if (pScheduleArray->count == 0)
|
||||
{
|
||||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"no schedule entry", __LINE__);
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
if (waiting_schedule_array.entries != NULL)
|
||||
if (pScheduleArray->count <= 0)
|
||||
{
|
||||
if (g_schedule_flag)
|
||||
logWarning("file: "__FILE__", line: %d, "
|
||||
"no schedule entry", __LINE__);
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
if (schedule_context == NULL)
|
||||
{
|
||||
if ((result=sched_thread_init_ex(&schedule_context)) != 0)
|
||||
{
|
||||
while (waiting_schedule_array.entries != NULL)
|
||||
{
|
||||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"waiting for schedule array ready ...", __LINE__);
|
||||
sleep(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if ((result=sched_append_array(pScheduleArray,
|
||||
&waiting_schedule_array)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
PTHREAD_MUTEX_LOCK(&schedule_context->lock);
|
||||
do {
|
||||
old_count = waiting_schedule_array.count;
|
||||
if ((result=sched_append_array(pScheduleArray,
|
||||
&waiting_schedule_array)) != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
return sched_init_entries(&waiting_schedule_array);
|
||||
newStart = waiting_schedule_array.entries + old_count;
|
||||
if ((result=sched_init_entries(newStart, pScheduleArray->count)) != 0)
|
||||
{
|
||||
waiting_schedule_array.count = newStart -
|
||||
waiting_schedule_array.entries; //rollback
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
PTHREAD_MUTEX_UNLOCK(&schedule_context->lock);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int sched_del_entry(const int id)
|
||||
|
|
@ -661,24 +698,10 @@ int sched_del_entry(const int id)
|
|||
|
||||
int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
||||
const int stack_size, bool * volatile pcontinue_flag,
|
||||
ScheduleContext **ppContext)
|
||||
ScheduleContext *pContext)
|
||||
{
|
||||
int result;
|
||||
pthread_attr_t thread_attr;
|
||||
ScheduleContext *pContext;
|
||||
|
||||
pContext = (ScheduleContext *)malloc(sizeof(ScheduleContext));
|
||||
if (pContext == NULL)
|
||||
{
|
||||
result = errno != 0 ? errno : ENOMEM;
|
||||
logError("file: "__FILE__", line: %d, " \
|
||||
"malloc %d bytes failed, " \
|
||||
"errno: %d, error info: %s", \
|
||||
__LINE__, (int)sizeof(ScheduleContext), \
|
||||
result, STRERROR(result));
|
||||
return result;
|
||||
}
|
||||
memset(pContext, 0, sizeof(ScheduleContext));
|
||||
|
||||
if ((result=init_pthread_attr(&thread_attr, stack_size)) != 0)
|
||||
{
|
||||
|
|
@ -686,7 +709,7 @@ int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
|||
return result;
|
||||
}
|
||||
|
||||
if ((result=sched_dup_array(pScheduleArray, \
|
||||
if ((result=sched_dup_array(pScheduleArray,
|
||||
&(pContext->scheduleArray))) != 0)
|
||||
{
|
||||
free(pContext);
|
||||
|
|
@ -695,8 +718,9 @@ int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
|||
|
||||
if (timer_slot_count > 0)
|
||||
{
|
||||
if ((result=fast_mblock_init(&pContext->mblock,
|
||||
sizeof(FastDelayTask), mblock_alloc_once)) != 0)
|
||||
if ((result=fast_mblock_init_ex1(&pContext->delay_task_allocator,
|
||||
"sched-delay-task", sizeof(FastDelayTask),
|
||||
mblock_alloc_once, 0, NULL, NULL, true)) != 0)
|
||||
{
|
||||
free(pContext);
|
||||
return result;
|
||||
|
|
@ -709,7 +733,9 @@ int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
|||
free(pContext);
|
||||
return result;
|
||||
}
|
||||
if ((result=init_pthread_lock(&pContext->delay_queue.lock)) != 0)
|
||||
|
||||
if ((result=fc_queue_init(&pContext->delay_queue, (long)
|
||||
(&((FastDelayTask *)NULL)->next))) != 0)
|
||||
{
|
||||
free(pContext);
|
||||
return result;
|
||||
|
|
@ -728,7 +754,6 @@ int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
|||
__LINE__, result, STRERROR(result));
|
||||
}
|
||||
|
||||
*ppContext = pContext;
|
||||
pthread_attr_destroy(&thread_attr);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -736,8 +761,16 @@ int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
|||
int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
||||
const int stack_size, bool * volatile pcontinue_flag)
|
||||
{
|
||||
int result;
|
||||
if (schedule_context == NULL)
|
||||
{
|
||||
if ((result=sched_thread_init_ex(&schedule_context)) != 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return sched_start_ex(pScheduleArray, ptid, stack_size,
|
||||
pcontinue_flag, &schedule_context);
|
||||
pcontinue_flag, schedule_context);
|
||||
}
|
||||
|
||||
void sched_set_delay_params(const int slot_count, const int alloc_once)
|
||||
|
|
@ -765,6 +798,8 @@ int sched_add_delay_task_ex(ScheduleContext *pContext, TaskFunc task_func,
|
|||
void *func_args, const int delay_seconds, const bool new_thread)
|
||||
{
|
||||
FastDelayTask *task;
|
||||
bool notify;
|
||||
|
||||
if (!pContext->timer_init)
|
||||
{
|
||||
logError("file: "__FILE__", line: %d, "
|
||||
|
|
@ -773,7 +808,8 @@ int sched_add_delay_task_ex(ScheduleContext *pContext, TaskFunc task_func,
|
|||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
task = (FastDelayTask *)fast_mblock_alloc_object(&pContext->mblock);
|
||||
task = (FastDelayTask *)fast_mblock_alloc_object(
|
||||
&pContext->delay_task_allocator);
|
||||
if (task == NULL)
|
||||
{
|
||||
return ENOMEM;
|
||||
|
|
@ -791,18 +827,7 @@ int sched_add_delay_task_ex(ScheduleContext *pContext, TaskFunc task_func,
|
|||
task->timer.expires = g_current_time;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&pContext->delay_queue.lock);
|
||||
if (pContext->delay_queue.head == NULL)
|
||||
{
|
||||
pContext->delay_queue.head = task;
|
||||
}
|
||||
else
|
||||
{
|
||||
pContext->delay_queue.tail->next = task;
|
||||
}
|
||||
pContext->delay_queue.tail = task;
|
||||
pthread_mutex_unlock(&pContext->delay_queue.lock);
|
||||
|
||||
fc_queue_push_ex(&pContext->delay_queue, task, ¬ify);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -816,18 +841,10 @@ int sched_add_delay_task(TaskFunc task_func, void *func_args,
|
|||
static void sched_deal_task_queue(ScheduleContext *pContext)
|
||||
{
|
||||
FastDelayTask *task;
|
||||
struct fc_queue_info qinfo;
|
||||
|
||||
pthread_mutex_lock(&pContext->delay_queue.lock);
|
||||
if (pContext->delay_queue.head == NULL)
|
||||
{
|
||||
pthread_mutex_unlock(&pContext->delay_queue.lock);
|
||||
return;
|
||||
}
|
||||
task = pContext->delay_queue.head;
|
||||
pContext->delay_queue.head = NULL;
|
||||
pContext->delay_queue.tail = NULL;
|
||||
pthread_mutex_unlock(&pContext->delay_queue.lock);
|
||||
|
||||
fc_queue_try_pop_to_queue(&pContext->delay_queue, &qinfo);
|
||||
task = qinfo.head;
|
||||
while (task != NULL)
|
||||
{
|
||||
fast_timer_add(&pContext->timer, (FastTimerEntry *)task);
|
||||
|
|
@ -846,6 +863,10 @@ static void *sched_call_delay_func(void *args)
|
|||
ScheduleContext *pContext;
|
||||
FastDelayTask *task;
|
||||
|
||||
#ifdef OS_LINUX
|
||||
prctl(PR_SET_NAME, "sched-delay");
|
||||
#endif
|
||||
|
||||
delay_context = (struct delay_thread_context *)args;
|
||||
task = delay_context->task;
|
||||
pContext = delay_context->schedule_context;
|
||||
|
|
@ -859,7 +880,7 @@ static void *sched_call_delay_func(void *args)
|
|||
logDebug("file: "__FILE__", line: %d, " \
|
||||
"delay thread exit, task args: %p", __LINE__, task->func_args);
|
||||
|
||||
fast_mblock_free_object(&pContext->mblock, task);
|
||||
fast_mblock_free_object(&pContext->delay_task_allocator, task);
|
||||
pthread_detach(pthread_self());
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -883,7 +904,7 @@ static void deal_timeout_tasks(ScheduleContext *pContext, FastTimerEntry *head)
|
|||
if (!task->new_thread)
|
||||
{
|
||||
task->task_func(task->func_args);
|
||||
fast_mblock_free_object(&pContext->mblock, task);
|
||||
fast_mblock_free_object(&pContext->delay_task_allocator, task);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -905,13 +926,13 @@ static void deal_timeout_tasks(ScheduleContext *pContext, FastTimerEntry *head)
|
|||
}
|
||||
else
|
||||
{
|
||||
usleep(1*1000);
|
||||
fc_sleep_ms(1);
|
||||
for (i=1; !task->thread_running && i<100; i++)
|
||||
{
|
||||
logDebug("file: "__FILE__", line: %d, "
|
||||
"task args: %p, waiting thread ready, count %d",
|
||||
__LINE__, task->func_args, i);
|
||||
usleep(1*1000);
|
||||
fc_sleep_ms(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -942,3 +963,16 @@ uint32_t sched_generate_next_id()
|
|||
{
|
||||
return ++next_id;
|
||||
}
|
||||
|
||||
static int sched_free_ptr_func(void *ptr)
|
||||
{
|
||||
free(ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sched_delay_free_ptr(void *ptr, const int delay_seconds)
|
||||
{
|
||||
const bool new_thread = false;
|
||||
return sched_add_delay_task_ex(schedule_context, sched_free_ptr_func,
|
||||
ptr, delay_seconds, new_thread);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
/**
|
||||
* Copyright (C) 2008 Happy Fish / YuQing
|
||||
*
|
||||
* FastDFS may be copied only under the terms of the GNU General
|
||||
* Public License V3, which may be found in the FastDFS source kit.
|
||||
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
|
||||
**/
|
||||
/*
|
||||
* Copyright (c) 2020 YuQing <384681@qq.com>
|
||||
*
|
||||
* This program is free software: you can use, redistribute, and/or modify
|
||||
* it under the terms of the Lesser GNU General Public License, version 3
|
||||
* or later ("LGPL"), as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* You should have received a copy of the Lesser GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SCHED_THREAD_H_
|
||||
#define _SCHED_THREAD_H_
|
||||
|
|
@ -15,6 +22,7 @@
|
|||
#include "common_define.h"
|
||||
#include "fast_timer.h"
|
||||
#include "fast_mblock.h"
|
||||
#include "fc_queue.h"
|
||||
|
||||
typedef int (*TaskFunc) (void *args);
|
||||
|
||||
|
|
@ -58,55 +66,59 @@ typedef struct fast_delay_task {
|
|||
struct fast_delay_task *next;
|
||||
} FastDelayTask;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FastDelayTask *head;
|
||||
FastDelayTask *tail;
|
||||
pthread_mutex_t lock;
|
||||
} FastDelayQueue;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ScheduleArray scheduleArray;
|
||||
ScheduleEntry *head; //schedule chain head
|
||||
ScheduleEntry *tail; //schedule chain tail
|
||||
|
||||
struct fast_mblock_man mblock; //for timer entry
|
||||
struct fast_mblock_man delay_task_allocator; //for FastDelayTask
|
||||
FastTimer timer; //for delay task
|
||||
bool timer_init;
|
||||
FastDelayQueue delay_queue;
|
||||
struct fc_queue delay_queue;
|
||||
pthread_mutex_t lock;
|
||||
|
||||
bool *pcontinue_flag;
|
||||
} ScheduleContext;
|
||||
|
||||
#define INIT_SCHEDULE_ENTRY(schedule_entry, _id, _hour, _minute, _second, \
|
||||
_interval, _task_func, _func_args) \
|
||||
#define INIT_SCHEDULE_ENTRY1(schedule_entry, _id, _hour, _minute, _second, \
|
||||
_interval, _task_func, _func_args, _new_thread) \
|
||||
(schedule_entry).id = _id; \
|
||||
(schedule_entry).time_base.hour = _hour; \
|
||||
(schedule_entry).time_base.minute = _minute; \
|
||||
(schedule_entry).time_base.second = _second; \
|
||||
(schedule_entry).interval = _interval; \
|
||||
(schedule_entry).task_func = _task_func; \
|
||||
(schedule_entry).new_thread = false; \
|
||||
(schedule_entry).func_args = _func_args
|
||||
(schedule_entry).func_args = _func_args; \
|
||||
(schedule_entry).new_thread = _new_thread
|
||||
|
||||
#define INIT_SCHEDULE_ENTRY_EX(schedule_entry, _id, _time_base, \
|
||||
_interval, _task_func, _func_args) \
|
||||
#define INIT_SCHEDULE_ENTRY_EX1(schedule_entry, _id, _time_base, \
|
||||
_interval, _task_func, _func_args, _new_thread) \
|
||||
(schedule_entry).id = _id; \
|
||||
(schedule_entry).time_base = _time_base; \
|
||||
(schedule_entry).interval = _interval; \
|
||||
(schedule_entry).task_func = _task_func; \
|
||||
(schedule_entry).new_thread = false; \
|
||||
(schedule_entry).func_args = _func_args
|
||||
(schedule_entry).func_args = _func_args; \
|
||||
(schedule_entry).new_thread = _new_thread
|
||||
|
||||
#define INIT_SCHEDULE_ENTRY(schedule_entry, _id, _hour, _minute, _second, \
|
||||
_interval, _task_func, _func_args) \
|
||||
INIT_SCHEDULE_ENTRY1(schedule_entry, _id, _hour, _minute, _second, \
|
||||
_interval, _task_func, _func_args, false)
|
||||
|
||||
#define INIT_SCHEDULE_ENTRY_EX(schedule_entry, _id, _time_base, \
|
||||
_interval, _task_func, _func_args) \
|
||||
INIT_SCHEDULE_ENTRY_EX1(schedule_entry, _id, _time_base, \
|
||||
_interval, _task_func, _func_args, false)
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern volatile bool g_schedule_flag; //schedule continue running flag
|
||||
extern volatile int g_schedule_flag; //schedule continue running flag
|
||||
extern volatile time_t g_current_time; //the current time
|
||||
|
||||
|
||||
#define get_current_time() (g_schedule_flag ? g_current_time: time(NULL))
|
||||
|
||||
/** generate next id
|
||||
|
|
@ -155,18 +167,35 @@ int sched_add_delay_task_ex(ScheduleContext *pContext, TaskFunc task_func,
|
|||
int sched_add_delay_task(TaskFunc task_func, void *func_args,
|
||||
const int delay_seconds, const bool new_thread);
|
||||
|
||||
|
||||
/** delay free a pointer
|
||||
* parameters:
|
||||
* ptr: the ptr to free
|
||||
* delay_seconds: delay seconds to free the ptr
|
||||
* return: error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int sched_delay_free_ptr(void *ptr, const int delay_seconds);
|
||||
|
||||
|
||||
/** init the schedule context
|
||||
* parameters:
|
||||
* pContext: store the ScheduleContext pointer
|
||||
* return: error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int sched_thread_init_ex(ScheduleContext **ppContext);
|
||||
|
||||
/** execute the schedule thread
|
||||
* parameters:
|
||||
* pScheduleArray: the schedule tasks
|
||||
* ptid: store the schedule thread id
|
||||
* stack_size: set thread stack size (byes)
|
||||
* pcontinue_flag: main process continue running flag
|
||||
* ppContext: store the ScheduleContext pointer
|
||||
* pContext: the ScheduleContext pointer
|
||||
* return: error no, 0 for success, != 0 fail
|
||||
*/
|
||||
int sched_start_ex(ScheduleArray *pScheduleArray, pthread_t *ptid,
|
||||
const int stack_size, bool * volatile pcontinue_flag,
|
||||
ScheduleContext **ppContext);
|
||||
ScheduleContext *pContext);
|
||||
|
||||
int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid, \
|
||||
const int stack_size, bool * volatile pcontinue_flag);
|
||||
|
|
@ -177,6 +206,9 @@ int sched_start(ScheduleArray *pScheduleArray, pthread_t *ptid, \
|
|||
*/
|
||||
void sched_print_all_entries();
|
||||
|
||||
time_t sched_make_first_call_time(struct tm *tm_current,
|
||||
const TimeInfo *time_base, const int interval);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue