rdkafka-hs-1.1.0.0: test-integration/cbits/metadata.c
/* Test-only observations of real native metadata/group-list ownership. */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <librdkafka/rdkafka.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define REAL(name) __typeof__(&name) real = (__typeof__(&name))dlsym(RTLD_NEXT, #name); \
if (!real) { fprintf(stderr, "metadata: %s\n", dlerror()); abort(); }
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t condition = PTHREAD_COND_INITIALIZER;
static int enabled, mode, entered, released, stats[5];
static const struct rd_kafka_metadata *metadata, *control_metadata;
static const struct rd_kafka_group_list *groups;
static const char *states[] = {"PreparingRebalance", "AwaitingSync", "CompletingRebalance", "Stable", "Dead", "Empty", "FutureState"};
void rdkafka_hs_test_metadata_begin(int value) {
pthread_mutex_lock(&lock); enabled = 1; mode = value; entered = released = 0;
metadata = control_metadata = NULL; groups = NULL;
for (int i = 0; i < 5; ++i) stats[i] = 0;
pthread_mutex_unlock(&lock);
}
int rdkafka_hs_test_metadata_stat(int index) {
pthread_mutex_lock(&lock); int result = index >= 0 && index < 5 ? stats[index] : -1;
pthread_mutex_unlock(&lock); return result;
}
int rdkafka_hs_test_metadata_entered(void) {
pthread_mutex_lock(&lock); int result = entered;
pthread_mutex_unlock(&lock); return result;
}
void rdkafka_hs_test_metadata_release(void) {
pthread_mutex_lock(&lock); released = 1;
pthread_cond_broadcast(&condition); pthread_mutex_unlock(&lock);
}
static void boundary(int kind) {
if (mode == kind) {
entered = 1;
while (!released) pthread_cond_wait(&condition, &lock);
}
}
rd_kafka_resp_err_t rd_kafka_metadata(rd_kafka_t *rk, int all_topics, rd_kafka_topic_t *only_topic,
const struct rd_kafka_metadata **result, int timeout_ms) {
REAL(rd_kafka_metadata);
rd_kafka_resp_err_t error = real(rk, all_topics, only_topic, result, timeout_ms);
pthread_mutex_lock(&lock);
if (enabled && error == RD_KAFKA_RESP_ERR_NO_ERROR) {
metadata = *result; stats[0]++;
if (mode == 3) {
if (metadata->topic_cnt != 1 || metadata->topics[0].partition_cnt != 2) abort();
metadata->topics[0].partitions[0].err = RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED;
}
if (mode == 19) {
struct rd_kafka_metadata *frame = (struct rd_kafka_metadata *)(void *)metadata;
if (frame->broker_cnt < 1 || frame->topic_cnt != 1 || frame->topics[0].partition_cnt != 1 || strlen(frame->brokers[0].host) < 2) abort();
/* Select one broker from the real, single-allocation native frame. The
* decoder control must work on both single-node and distributed brokers;
* metadata_destroy still owns and frees the complete allocation. */
frame->broker_cnt = 1;
frame->brokers[0].id = 7; frame->brokers[0].port = 19092;
strcpy(frame->brokers[0].host, "λ");
frame->orig_broker_id = 19;
frame->topics[0].err = RD_KAFKA_RESP_ERR_UNKNOWN_TOPIC_OR_PART;
struct rd_kafka_metadata_partition *partition = &frame->topics[0].partitions[0];
if (partition->replica_cnt != 1 || partition->isr_cnt != 1) abort();
partition->id = 42; partition->leader = 11;
partition->err = RD_KAFKA_RESP_ERR_TOPIC_AUTHORIZATION_FAILED;
partition->replicas[0] = 13; partition->isrs[0] = 17;
}
if (mode == 20) {
/* Metadata is one native allocation; destroy frees the entire frame. */
((struct rd_kafka_metadata *)(void *)metadata)->topic_cnt = 0;
}
boundary(1);
}
pthread_mutex_unlock(&lock); return error;
}
rd_kafka_resp_err_t rd_kafka_offsets_for_times(rd_kafka_t *rk, rd_kafka_topic_partition_list_t *partitions, int timeout_ms) {
REAL(rd_kafka_offsets_for_times);
pthread_mutex_lock(&lock);
int controlled = enabled && mode == 21;
if (controlled) stats[4]++;
pthread_mutex_unlock(&lock);
return controlled ? RD_KAFKA_RESP_ERR__TIMED_OUT : real(rk, partitions, timeout_ms);
}
void rd_kafka_metadata_destroy(const struct rd_kafka_metadata *value) {
REAL(rd_kafka_metadata_destroy);
pthread_mutex_lock(&lock);
if (enabled && value == metadata) { stats[1]++; metadata = NULL; }
pthread_mutex_unlock(&lock); real(value);
}
rd_kafka_resp_err_t rd_kafka_list_groups(rd_kafka_t *rk, const char *group,
const struct rd_kafka_group_list **result, int timeout_ms) {
REAL(rd_kafka_list_groups);
rd_kafka_resp_err_t error = real(rk, group, result, timeout_ms);
pthread_mutex_lock(&lock);
if (enabled && error == RD_KAFKA_RESP_ERR_NO_ERROR) {
groups = *result; stats[2]++;
if (mode >= 10 && mode <= 16) {
if (groups->group_cnt != 1) abort();
struct rd_kafka_group_info *info = &groups->groups[0];
free(info->state); info->state = strdup(states[mode - 10]);
}
if (mode == 18) {
if (groups->group_cnt != 1) abort();
groups->groups[0].err = RD_KAFKA_RESP_ERR_GROUP_AUTHORIZATION_FAILED;
}
if (mode == 17) {
if (groups->group_cnt != 1 || groups->groups[0].member_cnt != 1) abort();
struct rd_kafka_group_member_info *member = &groups->groups[0].members[0];
free(member->member_id); member->member_id = strdup("member-field-λ-配置");
free(member->client_id); member->client_id = strdup("client-field-λ-配置");
free(member->client_host); member->client_host = strdup("host-field-λ-配置");
const unsigned char metadata_bytes[] = {0, 1, 0, 255};
const unsigned char assignment_bytes[] = {255, 0, 2, 0, 3};
free(member->member_metadata); member->member_metadata = malloc(sizeof(metadata_bytes));
member->member_metadata_size = sizeof(metadata_bytes);
memcpy(member->member_metadata, metadata_bytes, sizeof(metadata_bytes));
free(member->member_assignment); member->member_assignment = malloc(sizeof(assignment_bytes));
member->member_assignment_size = sizeof(assignment_bytes);
memcpy(member->member_assignment, assignment_bytes, sizeof(assignment_bytes));
}
boundary(2);
}
pthread_mutex_unlock(&lock); return error;
}
void rd_kafka_group_list_destroy(const struct rd_kafka_group_list *value) {
REAL(rd_kafka_group_list_destroy);
pthread_mutex_lock(&lock);
if (enabled && value == groups) { stats[3]++; groups = NULL; }
pthread_mutex_unlock(&lock); real(value);
}
void rdkafka_hs_test_metadata_control(const char *brokers) {
rd_kafka_conf_t *conf = rd_kafka_conf_new(); char error[512];
if (rd_kafka_conf_set(conf, "bootstrap.servers", brokers, error, sizeof(error)) != RD_KAFKA_CONF_OK) abort();
rd_kafka_t *client = rd_kafka_new(RD_KAFKA_PRODUCER, conf, error, sizeof(error));
if (!client) abort();
const struct rd_kafka_metadata *value;
if (rd_kafka_metadata(client, 1, NULL, &value, 5000) != RD_KAFKA_RESP_ERR_NO_ERROR) abort();
control_metadata = value; rd_kafka_destroy(client);
}
void rdkafka_hs_test_metadata_end(void) {
REAL(rd_kafka_metadata_destroy);
pthread_mutex_lock(&lock);
const struct rd_kafka_metadata *control = control_metadata;
enabled = 0; released = 1; metadata = control_metadata = NULL; groups = NULL;
pthread_cond_broadcast(&condition); pthread_mutex_unlock(&lock);
if (control) real(control);
/* Ordinary results have Haskell ForeignPtr owners: never compete with their
* finalizers. A failing pre-owner cancellation probe terminates its process
* with the demonstrated native allocation still outstanding. */
}