Compare commits

...

491 Commits

Author SHA1 Message Date
vazmin 795b328bd6 gh actions: upgrade to 1.0.83-1 2025-11-23 10:47:37 +00:00
vazmin ac5f4a584f gh actions: upgrade to 1.0.83-1 2025-11-23 10:00:00 +00:00
vazmin c044906e63 gh actions: upgrade to 1.0.83-1 2025-11-23 09:05:57 +00:00
YuQing ccc84945d3 INSTALL changed 2025-11-18 14:47:17 +08:00
YuQing c4b6f1fcb5 upgrade version to 1.0.83 2025-11-16 17:00:25 +08:00
YuQing 017ca1efe7 comment out useless codes 2025-11-16 16:54:16 +08:00
YuQing 71b2229427 fast_task_queue.h: remove field finish_callback 2025-11-15 11:13:15 +08:00
YuQing 446fa6b815 libfastcommon.spec: correct expr statement 2025-11-12 16:39:36 +08:00
YuQing 98948c11bf downgrade required liburing from V2.5 to V2.4 2025-11-12 10:28:44 +08:00
YuQing d60b141a21 upgrade version to 1.0.82 2025-11-09 19:00:41 +08:00
YuQing 86bab518c6 remove compile warning under RockLinux 10 2025-11-09 18:19:20 +08:00
YuQing 4b9ef52da2 fix compile warning 2025-11-05 10:47:44 +08:00
YuQing e8a9967801 set use_io_uring explicitly 2025-11-04 15:34:25 +08:00
YuQing 96c896b09a INSTALL changed for v1.0.81 2025-10-30 15:38:27 +08:00
YuQing 8d9feff6e2 restore function ioevent_reset 2025-10-29 11:18:56 +08:00
YuQing cbcd38a9af upgrade version to 1.0,81 2025-10-26 12:26:07 +08:00
YuQing cd55792a89 Merge remote-tracking branch 'origin/use_iouring' 2025-10-26 12:24:55 +08:00
YuQing 511b1066c4 add macro function fc_string_equals_ex 2025-10-24 11:59:43 +08:00
YuQing ddf6b5dfe9 send zc done notify callback for recycling buffer 2025-10-19 20:10:12 +08:00
YuQing 23cd03bc76 add macro IPV4_ADDRESS_SIZE and IPV6_ADDRESS_SIZE 2025-10-18 15:48:31 +08:00
YuQing 065184a203 batch ioevent_uring_submit for RDMA network 2025-10-07 19:52:48 +08:00
YuQing de80dc19dc struct ioevent_puller add field service_name 2025-10-06 20:53:14 +08:00
YuQing dac653d694 IOEventCallback: change event type from short to int 2025-10-05 09:44:24 +08:00
YuQing d5dbe3d030 free_queue support parameter: need_shrink and set task->shrinked 2025-10-03 21:03:31 +08:00
YuQing 7973d81b69 struct fast_task_info add fields: is_client and op_type for io_uring 2025-10-03 10:18:25 +08:00
YuQing 4576f22e24 add function uring_prep_connect 2025-09-30 10:12:41 +08:00
YuQing cb6f6f13f3 support Linux io_uring OK 2025-09-27 15:37:56 +08:00
YuQing 012b2038ee add functions uring_prep_xxx 2025-09-25 14:49:37 +08:00
YuQing 48a0ea2e30 ioevent_set support io_uring 2025-09-24 15:54:03 +08:00
YuQing aa48e3cd9a upgrade version in spec file 2025-09-20 17:12:30 +08:00
YuQing 47fa7f99df ioevent.[hc] and ioevent_loop.[hc] support io_uring 2025-09-17 03:49:21 +08:00
YuQing ec8e47f831 tests/test_fast_buffer.c support all types 2025-09-12 14:08:46 +08:00
YuQing d9d6255621 test_fast_buffer.c support int2hex 2025-09-11 12:07:45 +08:00
YuQing 2f75958a4a test_fast_buffer.c support -t option 2025-09-11 10:05:25 +08:00
YuQing a4cae13e07 add function fc_ftoa 2025-09-10 15:06:17 +08:00
YuQing f136821c0d getIpaddrByNameEx: IPv4 has priority over IPv6 2025-09-10 15:05:15 +08:00
YuQing b97f23ced2 bytes_to_human_str support round off 2025-09-03 15:23:27 +08:00
YuQing 84a1f90a9a bytes to human readalbe string more gracefully 2025-08-29 13:25:58 +08:00
YuQing 3f19715e45 add function bytes_to_human_str 2025-08-29 11:36:10 +08:00
YuQing ce4c5e23d4 upgrade version to 1.0.79 2025-08-24 22:58:33 +08:00
YuQing d59da03d60 use size_t instead int to avoid compile warning 2025-08-21 20:52:35 +08:00
YuQing 8e51f4de3e logger.h export function log_it_ex3 2025-08-20 17:54:29 +08:00
vazmin 0afae48142 gh actions: upgrade to 1.0.78-1 2025-08-16 16:31:05 +00:00
YuQing 158924f259 upgrade version to 1.0.78 2025-08-14 09:43:38 +08:00
YuQing bf7c6e5144 add function fc_get_two/three_subdirs_full_filepath etc. 2025-08-13 15:40:39 +08:00
YuQing 6f4b3b7cd8 rename fc_combine_two_string to fc_combine_two_strings 2025-08-09 15:21:08 +08:00
YuQing b1f3c7894e add functions short2HEX, int2HEX, long2HEX 2025-08-08 21:49:43 +08:00
YuQing ec2db7cd33 replace sprintf and snprintf as necessary 2025-08-07 19:55:41 +08:00
YuQing 63ef9aa8f4 add functions short2hex, int2hex, long2hex etc. 2025-08-06 14:16:06 +08:00
YuQing 1d2f938a30 add null terminator after fc_itoa 2025-08-05 21:14:45 +08:00
YuQing e4898affdd fast_buffer support options: binary_mode and check_capacity 2025-08-05 11:44:04 +08:00
YuQing 558670bc63 performance opt.: replace sprintf as necessary 2025-08-04 17:34:57 +08:00
YuQing cf16c41054 src/connection_pool.[hc]: restore pthread mutex lock 2025-08-03 15:18:23 +08:00
YuQing 7fbb5c620b change int2buff, buff2int etc. functions to static inline 2025-08-03 15:10:28 +08:00
YuQing 9acc202481 connection_pool.[hc]: use CAS instead of pthread mutex lock 2025-08-02 19:16:03 +08:00
YuQing dd0d4dbc19 func conn_pool_get_key performance optimization 2025-07-26 16:58:25 +08:00
YuQing a1ae1cbcb0 add file: src/tests/cpool_benchmark.c 2025-07-26 15:02:23 +08:00
YuQing fda2679435 fast_mblock.[hc] remove counter: free node count 2025-07-22 16:37:36 +08:00
YuQing f0484579e0 tests/Makefile add mblock_benchmark 2025-07-18 11:21:18 +08:00
YuQing 6a18162a12 add src/tests/mblock_benchmark.c 2025-07-18 11:06:36 +08:00
YuQing 8e834f7165 src/spinlock.[hc]: use pthread spinlock 2025-07-18 11:04:53 +08:00
YuQing a256976600 fast_mblock.[hc] add counter: free node count 2025-07-09 09:20:41 +08:00
YuQing 62a29b55a5 spinlock functions return error no 2025-07-08 17:25:37 +08:00
YuQing a6dc24e2f3 add files: spinlock.[hc] 2025-07-07 17:30:43 +08:00
YuQing 70f6ad56ed getIpaddrByName: normalize ip addr when input addr is IPv4 or IPv6 2025-06-19 16:13:10 +08:00
vazmin 7f1a85b025 gh actions: upgrade to 1.0.77-1 2025-04-06 16:55:50 +00:00
YuQing eafb8aae74 upgrade version to 1.0.77 2025-04-01 16:48:17 +08:00
YuQing de1e9e7ec4 add function fc_compare_int64_ptr 2025-03-18 08:42:39 +08:00
YuQing aa144b5981 process_stop_ex add parameter: force 2025-02-19 15:07:48 +08:00
YuQing a0654b83c0 Merge branch 'master' of gitee.com:fastdfs100/libfastcommon 2025-02-03 20:09:51 +08:00
YuQing 19dcd0c5c4 impl. shorten_path for /./ and /../ 2025-02-03 20:07:29 +08:00
YuQing d764571f6e upgrade version to 1.0.76 2025-01-27 20:49:05 +08:00
YuQing 13fc696432 conn_pool_get_connection_ex add parameter: shared 2025-01-27 10:55:26 +08:00
YuQing e4a9fccddb set extra_params for socket gracefully 2025-01-26 13:02:07 +08:00
YuQing 5477593ce8 log error on thread local hashtable exception 2025-01-26 11:43:38 +08:00
YuQing ce0c23358f fix src/tests/Makefile under fedora 40 2024-12-18 11:48:58 +08:00
YuQing f4020e7622 remove compile warning under some gcc versions 2024-12-08 09:26:37 +08:00
YuQing ed65725833 explicit cast for fast_mblock_alloc_object 2024-11-21 10:21:07 +08:00
YuQing 13e213e3f8 small changes for src/system_info.c 2024-11-19 09:44:15 +08:00
YuQing 83f757672b add function is_rotational_device_by_path 2024-11-01 11:31:46 +08:00
YuQing 21366a4a2e add function get_statfs_by_path 2024-10-31 10:31:43 +08:00
YuQing aad48cc03d Merge branch 'master' of gitee.com:fastdfs100/libfastcommon 2024-10-31 09:37:09 +08:00
YuQing 7a108ec5a2 get_mounted_filesystems act as program df 2024-10-30 17:05:26 +08:00
vazmin 98e3471433 gh actions: upgrade to 1.0.75-1 2024-09-29 15:23:57 +00:00
YuQing 8a97e84e1c connection pool performance optimization 2024-09-22 12:19:50 +08:00
YuQing 8ce0119aa2 upgrade version to 1.0.75 2024-09-17 11:23:14 +08:00
YuQing 1b777792ab task init callback support extra argument 2024-09-15 12:03:18 +08:00
YuQing 2ab381d5e3 set comm_type for connection 2024-06-23 15:05:31 +08:00
vazmin a0f1ac59c8 gh actions: upgrade to 1.0.74-1 2024-06-15 14:45:01 +00:00
YuQing 13b31434e0 upgrade version to 1.0.74 2024-06-11 09:37:59 +08:00
YuQing 8aceec92ee add macro define ENODATA for other Unix 2024-05-20 08:22:35 +08:00
YuQing fef0a4a7f3 adapt to FreeBSD 13 2024-05-19 11:11:42 +08:00
YuQing 7f699688c0 add functions: get_log_level and get_log_level_caption 2024-04-08 15:15:34 +08:00
vazmin e0f47116c5 gh actions: upgrade to 1.0.73-1 2024-03-17 15:10:21 +00:00
YuQing 6cd9d6d842 upgrade version to V1.0.73 2024-03-11 11:17:47 +08:00
YuQing f4fef93061 add function is_loopback_ip 2024-03-05 16:28:04 +08:00
YuQing 226fd0d378 format ip address for IPv6 2024-03-05 10:58:09 +08:00
YuQing 9a720533ce server_id_func.[hc]: service group can overwrite buffer_size 2024-02-21 10:25:43 +08:00
YuQing 55ff532f92 struct fast_task_info remove fields: connect_timeout and network_timeout 2024-02-20 09:48:24 +08:00
YuQing d18ad54c2b add macro FC_SET_STRING_EMPTY 2024-02-15 16:44:00 +08:00
vazmin 2205cae6f3 gh actions: upgrade to 1.0.72-1 2024-01-31 11:59:25 +00:00
YuQing 5bceed4e32 upgrade version to 1.0.72 2024-01-30 10:48:31 +08:00
YuQing e0e7b9ef35 fc_queue.[hc]: add function fc_queue_remove 2024-01-21 09:22:43 +08:00
YuQing 05f3d62ee1 call fast_mblock_ref_counter_dec for delay free node correctly 2024-01-07 14:59:00 +08:00
vazmin 01f35da9d2 gh actions: upgrade to 1.0.71-1 2024-01-01 11:23:55 +00:00
YuQing 02f4659a32 log IPv6 address and port gracefully 2023-12-25 17:01:43 +08:00
YuQing 7816a28c53 use strtok_r instead of strtok for thread safety 2023-12-23 16:19:11 +08:00
YuQing 3f5eed3af2 upgrade version to 1.0.71 2023-12-08 15:22:51 +08:00
YuQing c9083ae0cf add file tests/test_memcpy.c 2023-12-04 21:41:08 +08:00
YuQing 5283a55bda tests/test_uniq_skiplist.c changed 2023-12-04 15:27:00 +08:00
YuQing 78caf9224b replace inet_ntop to getnameinfo for IPv6 2023-12-01 11:31:26 +08:00
YuQing dd77da144f field socket_domain rename to af 2023-11-29 18:35:00 +08:00
YuQing 700a5bcaec replace type in_addr_t to in_addr_64_t 2023-11-24 15:25:14 +08:00
YuQing 4eb30adb1a code adjust for pull request #47 2023-11-23 15:40:58 +08:00
YuQing 06f0ce67fd
Merge pull request #47 from sunqiangwei1988/master
Added: 增加IPv6支持
2023-11-23 08:53:32 +08:00
YuQing 7018f4e337
Merge branch 'master' into master 2023-11-23 08:53:07 +08:00
vazmin d68c9aff32 gh actions: upgrade to 1.0.70-3 2023-11-21 14:35:29 +00:00
YuQing 89e70977d5 logger.c: log_set_prefix and log_set_filename support re-entry 2023-11-21 15:56:51 +08:00
vazmin 5bda2dfef6 gh actions: upgrade to 1.0.70-2 2023-11-20 13:23:17 +00:00
vazmin 8b545fcfc0 gh actions: upgrade to 1.0.70-1 2023-11-19 14:45:34 +00:00
YuQing 6843acb456 add RDMA callback set_busy_polling 2023-11-18 10:40:37 +08:00
YuQing 894477753c upgrade version to 1.0.70 2023-11-16 11:04:18 +08:00
YuQing 6a5d4b1402 set comm_type to default correctly 2023-11-15 16:40:56 +08:00
YuQing 1c1ea296e7 global configs for communication and smart_polling etc. 2023-11-15 09:56:39 +08:00
YuQing 961ea11c4f struct fast_task_info add field pending_send_count 2023-11-06 10:51:08 +08:00
sunqiangwei1988 718906e477 Added: 增加IPv6支持
1、将IP_ADDRESS_SIZE的值由16修改为INET6_ADDRSTRLEN(46)。
2、新定义in_addr_64结构体替换in_addr结构体,以支持IPv6地址长度。
3、将connection_pool相关文件的socket_domain预设值由AF_INET修改为AF_UNSPEC。
4、增加IPv6的本地回环地址判断。
5、新增从字符串中解析IP地址和端口号方法(支持IPv4和IPv6)。
6、sockopt增加IPv6的支持
2023-11-02 10:13:42 +08:00
YuQing eafe769759 struct fast_task_queue support release callback 2023-10-12 21:53:29 +08:00
YuQing a1914ea249 add functions: fc_queue_push_with_check and fc_queue_peek 2023-09-30 14:46:56 +08:00
YuQing 61e07a4c0f add RDMA callbacks: send_done post_recv 2023-09-28 22:19:30 +08:00
YuQing 6151ea721b function conn_pool_set_rdma_extra_params_ex support double_buffers 2023-09-27 11:36:29 +08:00
YuQing 255defa788 rdma callback get_buffer rename to get_recv_buffer 2023-09-27 11:22:37 +08:00
YuQing acaf94db0c struct fast_task_info support send and recv double buffers 2023-09-25 18:36:15 +08:00
YuQing 2e176a9d1b send_by_buf1 and recv_data callback for rdma 2023-09-24 14:32:42 +08:00
YuQing e0b93756ab add functions: fc_server_[close|destroy]_connection 2023-09-23 11:06:56 +08:00
YuQing 7b0631e37a load connection_thread_local from cluster.conf 2023-09-20 10:43:05 +08:00
YuQing e0bbe89d23 connection_pool.[hc] support thread local for performance 2023-09-20 07:40:14 +08:00
YuQing 1c1cb6d5e7 restore epoll timeout when polling_queue is empty 2023-09-19 09:30:11 +08:00
YuQing 70c44ea490 nio thread data support busy_polling_callback 2023-09-18 16:17:34 +08:00
YuQing b4e5a26ba0 function ioevent_reset impl. for RDMA 2023-09-15 16:02:31 +08:00
YuQing f49c5d134a init extra_params when comm_type == fc_comm_type_sock 2023-09-14 09:55:59 +08:00
YuQing db49d54a37 conn_pool_alloc_connection impl. 2023-09-12 16:01:29 +08:00
YuQing c9687df03a ConnectionCallbacks struct changed 2023-09-12 07:53:34 +08:00
YuQing d24023aee7 function fc_alloc_rdma_pd impl. 2023-09-11 11:32:32 +08:00
YuQing 5139ec4682 connection_pool.[hc] support callbacks for RDMA 2023-09-10 20:54:24 +08:00
YuQing bc3a65ee19 add function fc_server_get_group_by_index 2023-09-07 09:34:45 +08:00
YuQing 44f827f291 server_id_func.[hc]: support communication type 2023-09-06 17:24:44 +08:00
YuQing 4a86162913 struct fast_task_info add field conn for RDMA connection 2023-09-05 09:20:07 +08:00
YuQing d5f6a192a5 type define for RDMA network 2023-09-04 08:51:35 +08:00
YuQing 05a694df77 get full mac address of infiniband NIC under Linux 2023-08-27 14:54:51 +08:00
vazmin 45e958cc1c gh actions: upgrade to 1.0.69-1 2023-08-06 07:21:50 +00:00
YuQing d9c14d602a upgrade version to 1.0.69 2023-08-06 09:03:40 +08:00
YuQing 4480669e03 uniq_skiplist support arg for free callback 2023-08-05 20:46:35 +08:00
YuQing fafbbb557e bugfixed: array_allocator_alloc MUST init the array 2023-08-02 14:59:57 +08:00
vazmin 1969fbba8d gh actions: upgrade to 1.0.68-1 2023-07-23 14:27:27 +00:00
YuQing 896b35603f add macros: MEM_ALIGN_FLOOR/CEIL_BY_MASK 2023-07-09 09:12:37 +08:00
YuQing 15facf395b upgrade version to 1.0.68 2023-07-07 08:25:18 +08:00
YuQing 3924213c9a sorted_queue.[hc]: pop_compare_func support argument 2023-07-05 16:39:57 +08:00
YuQing 643ecdc906 add functions sorted_queue_lock and sorted_queue_unlock 2023-06-29 16:47:30 +08:00
vazmin 7726d0223f gh actions: upgrade to 1.0.67-1 2023-06-04 10:51:06 +00:00
YuQing 4df1107fa3 add function fc_safe_writev 2023-05-29 18:15:59 +08:00
YuQing 0c588d965e bugfixed: fast_mblock_batch_alloc correct return value 2023-05-28 16:31:53 +08:00
YuQing 8de24ad5b5 add file src/tests/test_thread_local.c 2023-05-24 17:32:06 +08:00
YuQing 8cea8632d7 sorted_queue.[hc] support pop_compare_func 2023-05-23 20:13:38 +08:00
YuQing ccbc201636 bugfixed: MUST set tail->next to mblock->free_chain_head 2023-05-21 10:54:15 +08:00
YuQing e02bb4edc3 fast_mblock_init_ex2 add parameter prealloc_trunk_count 2023-05-19 11:20:30 +08:00
YuQing 085e06aac1 upgrade version to 1.0.67 2023-05-16 09:40:12 +08:00
YuQing 0806435fcc fast_allocator.c adapt fast_mblock_malloc_trunk_notify_func 2023-05-15 15:10:01 +08:00
YuQing c00a159fd3 fast_mblock_malloc_trunk_notify_func prototype changed 2023-05-14 20:06:27 +08:00
YuQing 5247caa71a uniq_skiplist_clear impl. more optimization 2023-05-09 07:45:11 +08:00
YuQing 2c5734ab22 add function uniq_skiplist_clear 2023-05-08 17:48:28 +08:00
YuQing a19119f962 sorted_queue_pop_all rename to sorted_queue_pop_to_chain 2023-05-05 08:09:08 +08:00
YuQing 428d13a07b sorted_queue.[hc]: sorted_queue_pop and sorted_queue_pop_all 2023-05-04 20:05:32 +08:00
YuQing 6dbc8b8937 sorted queue use double link chain for quick push 2023-05-04 17:08:51 +08:00
YuQing f1691b7480 lc_pair in struct fc_queue change to lcp 2023-03-27 16:18:32 +08:00
YuQing 595a8c5664 make.sh: link with -lrt when glibc versions less than 2.17 in Linux 2023-03-14 15:23:30 +08:00
vazmin 8b298570b3 gh actions: upgrade to 1.0.66-1 2023-02-18 05:43:58 +00:00
YuQing d81b75e4da upgrade version to 1.0.66 2023-02-15 21:06:06 +08:00
YuQing ee3631d426 struct fast_task_info remove field nio_stages.next 2023-02-12 20:00:48 +08:00
YuQing 6d3d082c6d add field notify_next for nio notify queue 2023-02-12 17:09:32 +08:00
YuQing c5138cc7cf struct fast_task_info add field: nio_stages.next for epoll edge trigger 2023-02-12 10:30:42 +08:00
vazmin 73ab695fc8 gh actions: upgrade to 1.0.65-1 2023-01-15 13:49:19 +00:00
YuQing be9c7c394a upgrade version to 1.0.65 2023-01-14 08:37:06 +08:00
YuQing 0113263e87 add function get_groups 2023-01-09 16:47:57 +08:00
YuQing fd8fbfe644 parse_bytes support space charactors 2023-01-04 12:20:45 +08:00
YuQing 8ab3420bce add function tcp_socket_connected 2022-12-30 17:18:28 +08:00
YuQing aa2fc62cbb add functions locked_list_move and locked_list_move_tail 2022-12-20 09:00:24 +08:00
YuQing ee70efcd09 fc_fallocate fail back to ftruncate under Linux 2022-12-11 12:43:24 +08:00
vazmin 86288bf99e gh actions: upgrade to 1.0.63-1 2022-11-21 14:54:56 +00:00
vazmin c0ea8349d3 debian: installation dir changes 2022-11-21 22:32:52 +08:00
YuQing 8ea9848047 upgrade version to 1.0.64 2022-11-21 08:15:28 +08:00
YuQing d07058934b bugfixed: can't use global malloc_allocator 2022-11-19 17:13:02 +08:00
YuQing 8e4adccb83 Makefile.in: force symlink library 2022-11-13 17:15:44 +08:00
YuQing 1eb603cfd1 bugfixed: common_blocked_queue_[alloc|free]_node must use lock 2022-11-10 08:47:07 +08:00
YuQing 22c7e31752 shared_func.[hc]: normalize_path use type string_t for general purpose 2022-11-07 08:27:43 +08:00
YuQing c18e864220 upgrade version to 1.0.63 2022-10-26 09:57:22 +08:00
YuQing 7289215470 sockopt.[hc]: getIpAndPort support ipv6 2022-10-17 15:58:27 +08:00
vazmin b52e516aee gh actions: upgrade to 1.0.62-1 2022-10-08 13:28:15 +00:00
YuQing 9c967d8d4b upgrade version to 1.0.62 2022-10-08 09:27:32 +08:00
YuQing 2b0796b166 fc_itoa small refine 2022-09-29 20:55:05 +08:00
YuQing 6ea757f492 add function fc_itoa 2022-09-28 22:13:57 +08:00
YuQing cf66174cf9 add function fc_sleep_us 2022-09-27 20:28:29 +08:00
vazmin 117b723274 fix: debian/rules Permission denied 2022-09-24 14:40:18 +08:00
vazmin 5e8dc1fcb5 gh actions: upgrade to 1.0.61-1 2022-09-22 12:22:09 +00:00
YuQing c416c6eeb0 upgrade version to 1.0.61 2022-09-22 09:12:38 +08:00
YuQing 88ad619902 add function common_blocked_queue_push_chain 2022-09-21 21:48:34 +08:00
YuQing 47fb7b2abd get_base_path_from_conf_file_ex support parameter: noent_log_level 2022-09-21 11:35:32 +08:00
vazmin 6b70919699 gh actions: upgrade to 1.0.60-1 2022-09-07 13:36:01 +00:00
YuQing a9b0f20f2d upgrade version to 1.0.60 2022-09-04 13:49:44 +08:00
YuQing 48ec9c64c6 add func fc_free_iovec_array 2022-09-04 11:29:13 +08:00
YuQing b0d57b325d export struct fast_allocator_wrapper 2022-08-28 17:20:20 +08:00
YuQing 82bbc013b2 fast_allocator.[hc] support object size 2022-08-27 21:24:16 +08:00
YuQing 7e52e7607a fast_allocator.[hc] support object callbacks 2022-08-27 09:49:20 +08:00
YuQing f47f136f56 add functions: iniGetDoubleCorrectValueEx and iniGetPercentCorrectValueEx 2022-08-26 11:02:36 +08:00
YuQing e11b22ad7d struct fast_task_info add field recv_body for dynamic recv buffer 2022-08-25 18:20:41 +08:00
YuQing ed66409220 normalize_path for base_path 2022-08-20 10:04:25 +08:00
vazmin 51715f26aa gh actions: upgrade to 1.0.59-1 2022-07-25 13:51:25 +00:00
YuQing 68360c1bd1 Merge branch 'replication_quorum' 2022-07-24 15:11:07 +08:00
YuQing 138e06fd6c upgrade version to V1.0.59 2022-07-24 14:57:30 +08:00
YuQing a9e82600b7 add function fc_get_first_lines 2022-07-21 18:30:53 +08:00
YuQing 599d0f1446 add global var g_set_cloexec and macro FC_SET_CLOEXEC 2022-06-25 11:23:43 +08:00
YuQing 4a7d852409 correct O_CLOEXEC to FD_CLOEXEC for F_SETFL 2022-06-25 09:22:25 +08:00
YuQing e254b8e1d3 open file with flag O_CLOEXEC 2022-06-24 18:52:39 +08:00
vazmin fe862d887e gh actions: upgrade to 1.0.58-1 2022-06-15 14:26:02 +00:00
YuQing aa5506191f add struct type FilenameFDPair 2022-06-12 21:24:37 +08:00
YuQing 5a90576bdc libfastcommon.spec: upgrade version 2022-06-06 20:35:56 +08:00
YuQing 7e5acf144b fast_mpool add stat fields: alloc_count, alloc_bytes and reset_count 2022-06-04 22:05:09 +08:00
YuQing 0b539bbba2 json decode supports memory pool for persistency 2022-06-04 12:41:07 +08:00
YuQing 009d33480f tests/test_json_parser.c OK. 2022-06-03 22:03:31 +08:00
YuQing 5f34bc872b add functions common_blocked_queue_empty/count 2022-06-03 15:30:49 +08:00
YuQing 793d683d2a add function fast_mblock_set_exceed_log_level 2022-05-31 18:39:52 +08:00
YuQing 64e9499de6 json decode support unicode 2022-05-29 21:13:00 +08:00
YuQing 275279a264 json_parser.[hc] refined for better performance 2022-05-29 10:55:16 +08:00
YuQing f24c558761 add function iniGetCharValueEx 2022-05-26 10:37:52 +08:00
YuQing 1f83e66306 add function conn_pool_get_connection_ex to support service name 2022-05-08 11:26:17 +08:00
YuQing 630a6a2af6 add function conn_pool_connect_server_ex1 to support service name 2022-05-07 16:53:02 +08:00
vazmin 23628e85f2 gh actions: upgrade to 1.0.57-1 2022-04-28 11:53:21 +00:00
YuQing 082902d28b add macro posix_fadvise for non-Linux 2022-04-24 08:13:44 +08:00
YuQing 5802203f9f upgrade version to V1.0.57 2022-04-22 14:55:55 +08:00
YuQing f836b1a9e2 bugfixed: fc_get_file_line_count_ex should rewind file 2022-04-14 16:48:32 +08:00
YuQing 26abf68ebd fast_mblock.[hc] support object destroy callback 2022-03-17 20:50:06 +08:00
YuQing ba011767f8 add functions: fc_get_path_child_count and fc_copy_file 2022-03-16 11:46:43 +08:00
YuQing 7d5e94f9dd rename fc_format_path to fc_remove_redundant_slashes 2022-03-15 08:51:20 +08:00
YuQing 80b751980b add function fc_format_path 2022-03-14 17:10:43 +08:00
vazmin 776a875c84 add changlog 1.0.56 2022-03-13 17:17:02 +08:00
YuQing 3fd3b167a8 small changes for logger.[hc] 2022-03-09 09:53:36 +08:00
YuQing 08f74db732 add function fc_check_rename_ex 2022-03-09 08:21:07 +08:00
YuQing 6836337d0a upgrade version to 1.0.56 2022-03-03 10:18:19 +08:00
YuQing 1cb1847b29 add function locked_list_destroy 2022-03-01 15:03:22 +08:00
YuQing c9cba5298a php-fastcommon compile OK 2022-02-25 14:48:19 +08:00
YuQing 7b9c257652 rename trim to fc_trim 2022-02-25 09:44:15 +08:00
YuQing 9f1d1b6d48 rename hash_xxx to fc_hash_xxx 2022-02-09 22:35:40 +08:00
YuQing 3331b927b3 add function log_try_init2 2022-02-14 09:41:21 +08:00
YuQing e9d186ce99 make.sh: generate macros for dirent fields 2022-02-10 22:18:53 +08:00
YuQing 29cc5af134 add function fc_iov_get_bytes 2022-02-02 20:37:13 +08:00
YuQing 21cd3a9798 make.sh refined 2022-02-02 09:32:07 +08:00
YuQing 3f20211a52 add const modifier for unification 2022-02-01 21:30:47 +08:00
YuQing a19a0071db sockopt.[hc] support tcpwritev and tcpreadv 2022-01-31 17:03:42 +08:00
YuQing 787eb3a7d6 NULL from parameter for getcwd 2022-01-29 17:18:13 +08:00
YuQing 4b9e2d6517 add function fc_gettid 2022-01-29 15:56:41 +08:00
YuQing 505893dc4c change include <sys/poll.h> to #include <poll.h> 2022-01-28 18:16:28 +08:00
YuQing 34f8c3abb9 correct pthread_rwlockattr getkind_np to setkind_np 2022-01-28 18:05:27 +08:00
YuQing a39005253b check pthread_rwlockattr_getkind_np for porting 2022-01-28 12:44:21 +08:00
vazmin 013b7888ea upgrade version to 1.0.55 2022-01-15 19:56:31 +08:00
YuQing f734710832 upgrade version to 1.0.55 2022-01-13 10:13:06 +08:00
YuQing 0410c7fedd add function sched_delay_free_ptr 2022-01-12 11:03:50 +08:00
YuQing 0381982ac2 function fast_mblock_batch_alloc changed 2022-01-09 15:26:27 +08:00
YuQing af68bf5d6a add macros: fc_queue_notify and fc_queue_notify_all 2022-01-06 20:32:05 +08:00
YuQing 7fbdb0cece add macros: ptr_array_allocator_xxx 2022-01-03 10:24:28 +08:00
YuQing c3f22aa867 fix arginfo_get_next_local_ip parameter 2021-12-31 22:41:57 +08:00
YuQing 89e1a99129 fastcommon php extension adapt to php 8 2021-12-31 03:15:38 +08:00
YuQing 59acf16fae php7_ext_wrapper.h adapt to php 8 2021-12-31 01:51:50 +08:00
vazmin 7fe16fd1b5 update debian package version 2021-12-27 21:34:40 +08:00
YuQing d9097001b5 mblock stat output support order by used ratio 2021-12-24 17:16:39 +08:00
YuQing d5d317f912 fix cmp_mblock_info for fast_mblock_manager_stat 2021-12-24 11:40:52 +08:00
YuQing b4f6152776 upgrade version to 1.0.54 2021-12-23 11:23:45 +08:00
YuQing 750c2c5e8a normalize_path removes prefix one ./ and multi ../ 2021-12-23 10:04:48 +08:00
YuQing fdb6bfb233 fix test_uniq_skiplist.c compile error 2021-12-16 09:44:16 +08:00
YuQing f6c5256264 fast path for sorted_array_insert 2021-12-14 21:18:30 +08:00
YuQing 0c437d3799 array_allocator.[hc] add parameter: need_lock 2021-12-14 21:15:30 +08:00
YuQing 64ae0757d7 pthread_rwlock_init: use NULL attr for non-Linux 2021-12-12 20:43:20 +08:00
YuQing 720c4a686d mblock add macro FAST_MBLOCK_MAGIC_CHECK for debug 2021-12-12 10:16:06 +08:00
YuQing 87377981ec sorted_queue.h: remove useless field: next_ptr_offset 2021-12-10 10:34:47 +08:00
YuQing 740272e303 typedef TaskContinueCallback function pointer 2021-11-28 10:34:01 +08:00
YuQing 4f29fd71eb sorted_array.[hc]: add function sorted_array_delete_by_index 2021-11-17 20:37:18 +08:00
YuQing 976872192a add macros id_name_array_allocator_xxx 2021-11-17 11:41:53 +08:00
YuQing b03963d4f6 add function sorted_array_find and marco sorted_id_name_array_init 2021-11-16 19:47:14 +08:00
YuQing ce2ee0f482 add macros FC_ATOMIC_SET_LARGER/SMALLER 2021-11-10 16:04:42 +08:00
YuQing 9ca9592326 shared_func.[hc]: add function fc_read_lines 2021-10-19 20:29:15 +08:00
YuQing 09e00bcf5e add functions fc_safe_write_file_init/open/close 2021-10-10 14:45:08 +08:00
YuQing a439b8e62d add macro FC_INIT_CHAIN and FC_IS_CHAIN_EMPTY 2021-10-09 20:54:35 +08:00
YuQing 8acd5e031b sched_thread.[hc]: add function sched_make_first_call_time 2021-09-27 15:51:45 +08:00
YuQing 2432e0bc79 add function fc_queue_alloc_chain 2021-09-22 09:25:28 +08:00
YuQing 1b35cbc094 sorted_queue.c: set notify correctly 2021-09-20 10:40:17 +08:00
YuQing 8491c5d155 add comments for sorted_array.h 2021-09-14 10:05:27 +08:00
YuQing 8717f85608 fast_allocator.c: optimize for the region with single allocator 2021-09-14 08:28:03 +08:00
YuQing 55f1e139a9 add file src/tests/test_sorted_array.c 2021-09-13 16:54:09 +08:00
YuQing 2993b34e80 add files: sorted_array.[hc] 2021-09-13 11:18:45 +08:00
YuQing 81950ac246 add function fc_queue_free_chain 2021-09-11 18:40:26 +08:00
YuQing 7614f789c8 add function sorted_queue_pop_to_queue_ex 2021-09-11 11:47:05 +08:00
YuQing f5fa33611f add files: array_allocator.[hc] 2021-09-10 16:36:14 +08:00
YuQing 6957c19992 fast_mblock.[hc]: add function fast_mblock_free_objects 2021-09-09 08:39:51 +08:00
YuQing a66370d0f8 add files: sorted_queue.[hc] 2021-09-02 21:05:09 +08:00
YuQing e1ef38d6a4 add type void_array_t 2021-08-20 11:01:50 +08:00
YuQing 45da326ce2 add types int32_array_t and int64_array_t 2021-08-19 19:28:15 +08:00
YuQing ebe7d87ca4 locked_list.h adds macros: locked_list_empty, locked_list_first_entry etc. 2021-08-17 15:39:01 +08:00
YuQing c6b2c32fe2 add function: fast_allocator_avail_memory 2021-08-17 10:01:42 +08:00
YuQing b7ecd0d4c4 add function init_pthread_rwlock 2021-08-13 21:10:05 +08:00
YuQing 2fafa215fd add function fc_queue_timedpeek 2021-08-11 11:32:23 +08:00
YuQing 44dcf4f821 add functions getFileContentEx1 and getFileContent1 2021-08-08 15:11:29 +08:00
YuQing 47c4eaeb13 fast_allocator.[hc]: correct reclaim_interval logic 2021-08-06 15:18:36 +08:00
zhiming 082a0fbc06 feat debian package
debian clear

deb clean rules

update the shared library install dirs in deb

Update rules

Update rules

Revert "Update rules"

This reverts commit 0d32aec147.
2021-08-03 08:57:12 +08:00
YuQing 3e0f1eb1fc fast_mblock_init_ex1: unify obj name 2021-07-20 20:27:48 +08:00
YuQing 1e9df48fec upgrade version to V1.0.53 2021-07-06 21:23:31 +08:00
YuQing 75b2aaee97 open for write MUST have the third parameter: mode 2021-07-01 07:41:29 +08:00
YuQing de879c15ee uniq_skiplist.h: add function uniq_skiplist_iterator_at 2021-06-30 15:05:31 +08:00
YuQing 3ad4a89ff5 process_action support action status 2021-06-26 11:00:52 +08:00
zhangchunsheng 3a61bf8074 Merge branch 'feature/debian' 2021-06-25 18:49:15 +08:00
zhangchunsheng df43a7d48b add debian pkg 2021-06-25 18:47:45 +08:00
zhangchunsheng d5f9271597 add debian 2021-06-25 18:23:16 +08:00
YuQing e10bb45605 correct macro MEM_ALIGN 2021-06-11 11:09:48 +08:00
YuQing a5ee277e2d add macros: MEM_ALIGN_FLOOR and MEM_ALIGN_CEIL 2021-06-11 11:03:26 +08:00
YuQing ba1f8eb38f add function fc_check_realloc_iovec_array 2021-06-08 14:15:24 +08:00
YuQing e6be76eb74 remove compile warning (the compiler is too worried) 2021-06-06 16:25:21 +08:00
YuQing 1d1a766c70 process_restart more gracefully 2021-06-04 17:56:02 +08:00
YuQing e068391c87 add function get_path_block_size and get_device_block_size 2021-06-02 11:07:28 +08:00
YuQing 48f108b40d add function fc_queue_pop_to_queue_ex 2021-06-01 17:08:24 +08:00
YuQing d5fd9a176d upgrade version to 1.0.52 2021-06-01 15:45:36 +08:00
YuQing 41681a5eb7 process_stop more gracefully (kill -9 on timeout) 2021-06-01 15:34:18 +08:00
YuQing da2077c6b8 add function get_kernel_version 2021-05-27 10:05:52 +08:00
YuQing 8174aed8e3 set errno to result ASAP for safety 2021-05-26 18:05:50 +08:00
YuQing ec181d51cf use macros: IS_URL_RESOURCE and IS_FILE_RESOURCE 2021-05-26 18:01:13 +08:00
YuQing 639d388c6d use libcurl to fetch URL resource 2021-05-26 15:21:55 +08:00
YuQing 768fbb68d4 normalize_path support file resource start with file:// 2021-05-26 09:38:04 +08:00
YuQing 6fddce73c5 add function normalize_path_ex and normalize_uri 2021-05-25 16:48:10 +08:00
YuQing df4fdfcab7 uniq_skiplist.[hc]: init function add parameter: allocator_use_lock 2021-05-20 10:08:01 +08:00
YuQing 797f4e08b8 fast_mblock.[hc]: support batch alloc and batch free 2021-05-18 15:06:59 +08:00
YuQing 4558290e1e add macro fc_fallocate 2021-05-11 09:07:23 +08:00
YuQing 5650e87665 upgrade version to V1.0.50 2021-04-30 10:59:59 +08:00
YuQing 63d57f82c6 set thread name OK 2021-04-28 07:54:57 +08:00
YuQing 082da383ff set thread name as necessary 2021-04-29 21:02:36 +08:00
YuQing 9d9cee76ac add functions fc_server_load_from_ini_context etc. 2021-04-28 20:31:00 +08:00
YuQing 1fd4a5da2b upgrade version to V1.0.49 2021-04-20 10:54:50 +08:00
YuQing f37c3bf013 parse_bytes function more graceful 2021-04-17 21:51:27 +08:00
YuQing 76ef22d380 add function locked_list_count 2021-04-12 15:31:59 +08:00
YuQing 6a3bcd4547 add file locked_list.h 2021-04-09 21:28:22 +08:00
YuQing c1bb9d6532 add functions: fc_queue_push_queue_to_tail etc. 2021-04-07 14:53:29 +08:00
YuQing 097a7db3cb add functions: fc_check_filename_ex 2021-03-28 21:08:20 +08:00
YuQing cc304e5d7a add functions: fc_string_case_compare, fc_string_case_equal etc. 2021-03-21 10:15:29 +08:00
YuQing 07ba689835 add FilenameString type and macro 2021-03-19 09:17:11 +08:00
YuQing 13de41bc05 add functions: fc_mkdirs and str_replace 2021-03-16 09:10:34 +08:00
YuQing 88aa31df07 add uniq_skiplist_pair struct and init function 2021-03-15 20:57:48 +08:00
YuQing 04226e28fc add macros: FC_ABS and FC_NEGATIVE 2021-03-10 14:38:09 +08:00
YuQing 3f15be8d92 correct tests compile error and warning 2021-02-25 15:09:35 +08:00
YuQing 0ed5b405d9 add test_mutex_lock_perf 2021-02-25 15:06:23 +08:00
YuQing 9665ff87e3 upgrade version to 1.0.48 2021-02-01 10:54:46 +08:00
YuQing 02701c3781 add function fc_queue_timedpop 2021-02-01 10:52:55 +08:00
YuQing 12aab5f94c small changes for logger.c 2021-02-01 09:27:18 +08:00
YuQing 35aa996333 add function common_blocked_queue_timedpop 2021-01-29 17:39:12 +08:00
YuQing 41a4ca78c7 fast_buffer.[hc]: add function fast_buffer_append_binary 2021-01-27 21:10:43 +08:00
YuQing e104d2f7f6 upgrade version to 1.0.47 2021-01-22 10:25:37 +08:00
YuQing aea8a6562d fc_atomic.h add FC_ATOMIC_GET, FC_ATOMIC_INC etc. 2021-01-15 18:05:39 +08:00
YuQing c822e92b19 fast_mblock.[hc]: support wait with element limit 2021-01-14 15:25:37 +08:00
YuQing 1f126476bc fc_atomic.h: add FC_ATOMIC_SET 2021-01-11 16:22:39 +08:00
YuQing 3b2b9d6176 upgrade version to 1.0.46 2020-12-31 20:55:28 +08:00
YuQing 10c037e32d bugfixed for rpm build 2020-12-31 20:53:57 +08:00
YuQing 7d5a540a2f INI_FIND_ITEM just break instead of return 2020-12-29 16:21:00 +08:00
YuQing 89c68e11db add function get_time_item_from_conf_ex 2020-12-29 15:15:49 +08:00
YuQing e96127a040 upgrade version to 1.0.45 2020-12-25 10:25:03 +08:00
YuQing 89abbd2ee4 use readdir instead of readdir_r in Linux 2020-12-24 18:34:16 +08:00
YuQing 7190fad3d0 add function uniq_skiplist_find_node_ex 2020-12-24 16:03:53 +08:00
YuQing 3e192fae09 fc_timedwait_ms must call get_current_time_ms() 2020-12-11 11:36:12 +08:00
YuQing 0cd65f088c tcp_quick_ack option for Linux 2020-12-06 16:59:09 +08:00
YuQing ff7109fcd4 add function: get_current_time_ns 2020-12-04 19:33:21 +08:00
YuQing 85354b6ef6 add functions: iniGetByteValueEx and iniGetIntCorrectValueEx 2020-12-02 18:10:45 +08:00
YuQing 5a04c1c656 timer_modify check new_expires 2020-12-02 13:57:28 +08:00
YuQing 0995f447cb bugfixed: cpool_node object pool must use lock 2020-12-01 21:05:45 +08:00
YuQing 3d74837527 add tests/test_data_visible.c 2020-11-30 10:50:29 +08:00
YuQing 93e6cec05d locked_timer: atomic opt for lock_index 2020-11-29 21:52:49 +08:00
YuQing b236808a69 set entry status and index correctly in add_entry 2020-11-26 22:41:29 +08:00
YuQing 1189c97d8e fast_timer.h restore to NO lock version 2020-11-26 17:26:43 +08:00
YuQing 8718818dc0 add locked_timer.[hc]: time wheel timer with lock 2020-11-26 16:18:37 +08:00
YuQing f5028fcbe0 fast_timer.[hc]: support lock for option 2020-11-23 11:45:33 +08:00
YuQing 58e1aea32b add function: fc_queue_empty 2020-11-03 22:09:42 +08:00
YuQing 5f9bfd8bae add function: iniGetPercentValueEx 2020-10-31 14:19:21 +08:00
YuQing 2e85b7ad9d add functions: fc_timedwait_xxx 2020-10-29 21:10:03 +08:00
YuQing 1ac8c8f3c2 fast_task_info add fields: reffer_count and continue_callback 2020-10-28 11:59:37 +08:00
YuQing d54007f79b check poll result for POLLHUP and POLLERR 2020-10-25 20:42:53 +08:00
YuQing bb33b2e09f notify field: add volatile modifier 2020-10-21 09:30:53 +08:00
YuQing 5b6bb3193b fast_task_queue.h: change nio_stage field 2020-10-19 16:35:36 +08:00
YuQing 56a5ac4f92 modify license to LGPLv3 and copyright 2020-10-14 11:03:46 +08:00
YuQing fa8f93b018 add function skiplist_get_proper_level 2020-10-10 11:11:42 +08:00
YuQing 0aab6a0531 correct spell iovent to ioevent :( 2020-09-30 18:57:08 +08:00
YuQing dc40977500 sched_thread.c: sched_add_entries gracefully 2020-09-29 22:18:30 +08:00
YuQing 118f1e2e2e correct printf format for port 2020-09-27 09:24:32 +08:00
YuQing 7d5512682b fixed printf format for port 2020-09-27 08:53:57 +08:00
YuQing 082a322f3d change port type to uint16_t 2020-09-27 08:46:46 +08:00
YuQing 55fc4013b3 add function fc_get_last_lines 2020-09-26 11:15:57 +08:00
YuQing dc43f8254a remove useless logInfo 2020-09-21 22:27:16 +08:00
YuQing 556b7337c4 add file src/fc_atomic.h 2020-09-14 15:05:38 +08:00
YuQing 23d8adf05f ini support retry gloabal section when item not exist 2020-09-13 13:20:41 +08:00
YuQing bc5efd235e add function fc_sleep_ms 2020-09-12 15:41:16 +08:00
YuQing 6a54076cf5 change field nio_stage from char to short 2020-09-09 12:26:43 +08:00
YuQing 488e483d22 schedule waiting_schedule_array use atomic opt 2020-09-08 18:53:08 +08:00
YuQing 130d7fe110 add func conn_pool_compare_ip_and_port 2020-09-08 09:06:26 +08:00
YuQing a749b84ce2 sockopt.[hc]: add function asyncconnectserverbyip 2020-09-06 18:49:15 +08:00
YuQing d9b914ea6e set rlim_max for setrlimit call 2020-09-04 09:09:14 +08:00
YuQing 98b816c625 fast_mblock.[hc]: support alloc elements limit 2020-09-01 21:22:13 +08:00
YuQing 6eb2d1c2e7 shared_func.[hc]: add function fc_path_contains 2020-08-31 16:02:10 +08:00
YuQing 3ff6cd8844 ini_file_reader.h: add struct ini_full_context 2020-08-29 15:44:17 +08:00
YuQing cc80f721f2 add functions: fc_get_first_line and fc_get_last_line 2020-08-24 11:35:31 +08:00
YuQing f776a818f4 correct old_value in the log info 2020-08-22 12:02:23 +08:00
YuQing c2d8faaba7 thread pool enhance 2020-08-05 15:00:37 +08:00
YuQing 4b085fbc09 add functions: get thread counters 2020-07-22 17:54:14 +08:00
YuQing 29586005ba add files: thread_pool.[hc] 2020-07-20 18:03:47 +08:00
YuQing 8e0f5794d9 add files: shared_buffer.[hc] 2020-07-10 21:44:10 +08:00
YuQing 4aad4f78b9 add files: fc_memory.[hc] 2020-07-07 10:32:16 +08:00
YuQing ec22ab681e bugfixed: uniq_addresses use FCAddressPtrArray 2020-06-28 19:26:42 +08:00
YuQing fb03bf23ba add fc_queue.[hc] 2020-06-01 16:38:48 +08:00
YuQing 473b1e7218 add function: fc_get_umask 2020-05-29 14:32:42 +08:00
YuQing 2cb3214d58 disable -Wformat-xxx when gcc version >= 7 2020-05-25 09:39:19 +08:00
YuQing 7187840167 add src/tests/test_file_lock.c 2020-05-24 15:11:31 +08:00
YuQing d1f1bf97e1 add function uniq_skiplist_replace_ex 2020-05-21 18:10:28 +08:00
YuQing 85aff01e5e test_crc32.c support offset and size 2020-05-17 15:54:41 +08:00
YuQing b9cae5de7f add macro PTHREAD_MUTEX_LOCK 2020-05-10 20:29:05 +08:00
YuQing 7149a54128 use mutex lock when access dynamic content array 2020-05-07 16:41:21 +08:00
YuQing 54cb8f8415 free_queue support init_callback 2020-05-06 16:04:01 +08:00
YuQing 87d7b1f55d add fc_list_first_entry 2020-05-05 11:13:00 +08:00
YuQing e86767c120 add function fc_list_add_before 2020-05-02 21:12:51 +08:00
YuQing 73dba84e82 uniq_skiplist_grow set prev link correctly 2020-04-27 14:16:00 +08:00
YuQing e4a5cadfe1 connection info support extra data 2020-04-26 20:19:46 +08:00
YuQing c8bf9f24ef connection_pool support validate connection on error 2020-04-26 11:12:52 +08:00
YuQing 4011fcb39e add test_file_write_hole.c 2020-04-24 22:15:41 +08:00
YuQing 688fcf4b74 uniq_skiplist add some macro defines 2020-04-22 20:27:21 +08:00
YuQing 759fd117d8 uniq_skiplist support bidirection 2020-04-21 09:31:04 +08:00
YuQing 4e53bd3e2d skiplist add function find_ge 2020-04-10 10:19:53 +08:00
YuQing 01b1841fda add function fc_get_file_line_count_ex 2020-03-31 10:10:49 +08:00
YuQing 201daee3b6 mblock_manager_stat_print format 2020-03-30 23:20:55 +08:00
YuQing c6ddfb98a1 correct int64_t compare result 2020-03-30 19:00:00 +08:00
YuQing 46da99bbba mblock stat type: int to int64_t 2020-03-30 18:44:34 +08:00
YuQing 0810cb4d21 bugfixed: allocator_array_check_capacity alloc size 2020-03-29 22:03:13 +08:00
YuQing 427818d005 fast_mblock stats refine 2020-03-29 12:30:42 +08:00
YuQing 590aa8d3a0 bugfixed: call fast_mblock_destroy in common_blocked_queue_destroy 2020-03-28 23:12:02 +08:00
YuQing 2df796589c add function common_blocked_queue_push_ex 2020-03-26 19:04:41 +08:00
YuQing d3b0c5dfb0 add function fast_buffer_set_capacity 2020-03-26 15:51:31 +08:00
YuQing c2837fde5f add functions getIpAndPort and getPeerIpAndPort 2020-03-26 11:36:44 +08:00
YuQing da4c9a2581 common_blocked_queue_return_nodes 2020-03-23 21:54:31 +08:00
YuQing 6d88bb980d bugfixed: must use lock when free_all_nodes 2020-03-22 13:21:20 +08:00
YuQing edb8b2c4dd sched_add_entries use temp ScheduleArray for rare case 2020-03-21 09:50:03 +08:00
YuQing 21336eee3e add function fc_init_buffer 2020-03-13 15:09:08 +08:00
YuQing 4d653d1b3e add functions: create_work_threads_ex and fc_create_thread 2020-03-12 10:36:51 +08:00
YuQing 3d0956d302 nio_thread_data support thread notify 2020-03-10 21:16:27 +08:00
YuQing f9881d96b7 add canceled field for complicated thread model 2020-03-09 10:51:46 +08:00
YuQing b88d5b03fe add waiting_queue for Linux eventfd notify 2020-03-08 16:12:54 +08:00
YuQing 6b528f123f add ctx pointer for libserverframe nio 2020-03-06 22:06:35 +08:00
YuQing d87d98dbe0 add function fast_char_unescape 2020-03-04 20:54:50 +08:00
YuQing 773f1bc28c shared_func.[hc]: add functions getFileSize 2020-03-03 10:19:53 +08:00
YuQing 8005b18198 fast_allocator.[hc]: add function fast_allocator_strdup 2020-02-27 22:26:17 +08:00
YuQing 3e3bcda2df add functions fc_floor_prime and fc_ceil_prime 2020-02-27 15:31:44 +08:00
YuQing 74bcd17360 in func writeToFile: change write to fc_safe_write 2020-02-25 21:25:29 +08:00
YuQing a57b87b556 common_blocked_queue support pop all nodes 2020-02-24 21:57:05 +08:00
YuQing faa1b6ddf2 multi_socket_client support timeout in ms 2020-02-22 15:53:50 +08:00
YuQing fd4368e6e2 bugfixed: use new_addr instead of addr 2020-02-21 20:45:20 +08:00
YuQing 6e5511614d add function fc_server_check_connect_ex 2020-02-20 16:03:28 +08:00
YuQing 89ad53974f add uniq server addresses 2020-02-19 15:49:39 +08:00
YuQing 11a441e1f8 server_id_func.[hc] OK 2020-02-17 16:25:42 +08:00
YuQing 81e6455819 add tests/test_server_id_func.c 2020-02-15 20:55:57 +08:00
YuQing 7459f7ded4 add files: server_id_func.[hc] 2020-02-14 21:21:42 +08:00
YuQing 5d3c3c576c add function fast_mpool_log_stats 2020-02-12 19:11:52 +08:00
YuQing d6b0f1b0c4 add function is_network_error 2020-02-09 17:17:37 +08:00
YuQing 798244cc1b add function fc_memrchr 2020-02-08 09:19:10 +08:00
YuQing 90e61572c0 fast_mblock.[hc]: add init_args for init_func 2020-02-03 22:42:27 +08:00
YuQing a6066181ae add function split_string_ex 2020-02-02 20:49:57 +08:00
YuQing aac2abcfa9 Merge branch 'master' of gitee.com:fastdfs100/libfastcommon 2020-01-21 10:08:24 +08:00
YuQing 1c7069068b add src/tests/test_uniq_skiplist.c 2020-01-21 10:06:48 +08:00
YuQing 91e0124ae5 add uniq_skiplist.[hc] 2020-01-19 22:40:46 +08:00
YuQing 916ad1f9e0 add test file src/tests/test_pthread_lock.c 2020-01-19 15:52:23 +08:00
happyfish100 b10c5c6294 add LICENSE. 2019-12-26 10:46:53 +08:00
YuQing c8c75666cf upgrade version to 1.43 2019-12-25 20:35:44 +08:00
YuQing 12dde730c8 add function fc_delete_file_ex 2019-12-25 15:20:07 +08:00
YuQing ad57015174 add buffered_file_writer.[hc] files 2019-12-25 10:47:07 +08:00
YuQing 2992bfc4be add buffered_file_writer.[hc] 2019-12-25 10:41:58 +08:00
YuQing 1302c5306a correct function skiplist_iterator in skiplist.h 2019-12-24 08:52:31 +08:00
YuQing 41e4e6f079 replace function call system to getExecResult 2019-12-06 11:49:35 +08:00
YuQing dd34251817 upgrade version to 1.0.42 2019-12-05 08:51:59 +08:00
YuQing b7dde81d1e function create_work_threads changed 2019-12-03 21:20:41 +08:00
YuQing 414f20fa61 init compress_log_days_before to 1 2019-12-01 20:34:12 +08:00
YuQing 22a6115c4e delete old file refine 2019-11-30 16:23:46 +08:00
YuQing d818e59009 add function get_gzip_command_filename 2019-11-30 15:33:59 +08:00
YuQing 938a6a7fac modify website name 2019-11-23 10:03:54 +08:00
YuQing b89d23ab8b INSTALL changed 2019-11-23 10:02:35 +08:00
YuQing aabc50c144 add .gitignore 2019-11-22 09:53:58 +08:00
162 changed files with 25473 additions and 5169 deletions

98
.gitignore vendored Normal file
View File

@ -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
View File

@ -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
View File

@ -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

165
LICENSE Normal file
View File

@ -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
View File

@ -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

6
debian/README.Debian vendored Normal file
View File

@ -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

192
debian/changelog vendored Normal file
View File

@ -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

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
11

25
debian/control vendored Normal file
View File

@ -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.

530
debian/copyright vendored Normal file
View File

@ -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.

1
debian/libfastcommon-dev.install vendored Normal file
View File

@ -0,0 +1 @@
usr/include/fastcommon/*

1
debian/libfastcommon.install vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/libfastcommon.so*

14
debian/rules vendored Executable file
View File

@ -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

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (quilt)

3
debian/watch vendored Normal file
View File

@ -0,0 +1,3 @@
version=3
opts="mode=git" https://github.com/happyfish100/libfastcommon.git \
refs/tags/v([\d\.]+) debian uupdate

View File

@ -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
View File

@ -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

View File

@ -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, "

View File

@ -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

View File

@ -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)

129
src/array_allocator.c Normal file
View File

@ -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);
}

174
src/array_allocator.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

214
src/buffered_file_writer.c Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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));
}

View File

@ -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, &notify)) == 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, &notify);
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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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);

77
src/fc_atomic.h Normal file
View File

@ -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

View File

@ -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)

20
src/fc_memory.c Normal file
View File

@ -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;

112
src/fc_memory.h Normal file
View File

@ -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

343
src/fc_queue.c Normal file
View File

@ -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;
}

235
src/fc_queue.h Normal file
View File

@ -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, &notify);
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, &notify);
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, &notify);
}
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, &notify);
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, &notify);
}
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, &notify);
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, &notify);
}
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

View File

@ -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)
{

View File

@ -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;

View File

@ -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)

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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回环地址时为了简化系统的改动
IPv6IPv4
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;
}

View File

@ -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

View File

@ -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

View File

@ -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>

View File

@ -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_

View File

@ -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
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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回环地址时为了简化系统的改动
IPv6IPv4
*/
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);
}
}
}

View File

@ -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);

115
src/locked_list.h Normal file
View File

@ -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

353
src/locked_timer.c Normal file
View File

@ -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;
}

102
src/locked_timer.h Normal file
View File

@ -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

View File

@ -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(&current_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(&current_time, &tm);
}
}
localtime_r(&current_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

View File

@ -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:

View File

@ -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;
}

View File

@ -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
}

View File

@ -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)
{

View File

@ -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;

View File

@ -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) {

View File

@ -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);
/**

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

View File

@ -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_ */

View File

@ -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, &notify);
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);
}

View File

@ -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

1818
src/server_id_func.c Normal file

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