packages feed

rdkafka-hs-1.1.0.0: test-integration/cbits/transaction_ownership.c

/* Integration executable only. Count returned native transaction errors, not
 * background allocations. Ordinary calls reach the native library. */
#define _GNU_SOURCE
#include <dlfcn.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <librdkafka/rdkafka.h>

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t resume = PTHREAD_COND_INITIALIZER;
static int enabled, mode, released;
static int64_t counts[13];
static struct { rd_kafka_error_t *pointer; int live; } errors[4096];
static rd_kafka_t *last_client;

#define REAL(name) __typeof__(&name) real = (__typeof__(&name))dlsym(RTLD_NEXT, #name); \
  if (!real) { fprintf(stderr, "transaction seam: %s\n", dlerror()); abort(); }

void rdkafka_hs_test_tx_begin(int value) {
  pthread_mutex_lock(&lock);
  enabled = 1; mode = value; released = 0; last_client = NULL;
  for (int i = 0; i < 13; ++i) counts[i] = 0;
  for (int i = 0; i < 4096; ++i) { errors[i].pointer = NULL; errors[i].live = 0; }
  pthread_mutex_unlock(&lock);
}
void rdkafka_hs_test_tx_release(void) {
  pthread_mutex_lock(&lock);
  released = 1; pthread_cond_broadcast(&resume);
  pthread_mutex_unlock(&lock);
}
void rdkafka_hs_test_tx_end(void) {
  REAL(rd_kafka_error_destroy);
  pthread_mutex_lock(&lock);
  enabled = 0; released = 1; last_client = NULL;
  /* Clean up the deliberately leaking control, or a failed assertion. */
  for (int i = 0; i < 4096; ++i) {
    if (errors[i].live) real(errors[i].pointer);
    errors[i].pointer = NULL; errors[i].live = 0;
  }
  pthread_cond_broadcast(&resume);
  pthread_mutex_unlock(&lock);
}
int64_t rdkafka_hs_test_tx_stat(int index) {
  pthread_mutex_lock(&lock);
  int64_t result = index >= 0 && index < 13 ? counts[index] : -1;
  pthread_mutex_unlock(&lock);
  return result;
}
static rd_kafka_error_t *track(rd_kafka_t *client, rd_kafka_error_t *error) {
  pthread_mutex_lock(&lock);
  if (enabled) {
    last_client = client; counts[10]++;
    if (error) {
      int i;
      for (i = 0; i < 4096 && errors[i].live; ++i) {}
      if (i == 4096) abort();
      errors[i].pointer = error; errors[i].live = 1; counts[0]++;
    }
  }
  pthread_mutex_unlock(&lock);
  return error;
}
/* Called by the one shared error-destroy interposer in seek_faults.c. */
void rdkafka_hs_tx_error_destroyed(rd_kafka_error_t *error) {
  pthread_mutex_lock(&lock);
  if (enabled && error) for (int i = 0; i < 4096; ++i) {
    if (errors[i].pointer == error && errors[i].live) {
      errors[i].live = 0; counts[1]++; break;
    }
  }
  pthread_mutex_unlock(&lock);
}
static void reading(const rd_kafka_error_t *error, int index) {
  pthread_mutex_lock(&lock);
  int known = 0, live = 0;
  if (enabled && error) for (int i = 0; i < 4096; ++i) {
    if (errors[i].pointer == error) { known = 1; live |= errors[i].live; }
  }
  if (known) {
    counts[index]++;
    if (!live) counts[8]++;
    if (mode == 18 + index && counts[9] == 0) {
      counts[9]++;
      while (!released) pthread_cond_wait(&resume, &lock);
    }
  }
  pthread_mutex_unlock(&lock);
}
static int current_mode(void) {
  pthread_mutex_lock(&lock);
  int value = enabled ? mode : 0;
  pthread_mutex_unlock(&lock);
  return value;
}
static const char *flag_setter_names[] = {"rd_kafka_error_set_fatal", "rd_kafka_error_set_retriable", "rd_kafka_error_set_txn_requires_abort"};
int rdkafka_hs_test_tx_flag_diagnostics_available(void) {
  for (int bit = 0; bit < 3; ++bit) if (!dlsym(RTLD_NEXT, flag_setter_names[bit])) return 0;
  return 1;
}
static rd_kafka_error_t *fixture(int value) {
  rd_kafka_error_t *error = rd_kafka_error_new(value == 200 ? (rd_kafka_resp_err_t)32767 : RD_KAFKA_RESP_ERR__STATE,
                                             "%s", "native transaction fixture");
  /* These actual native setters are private diagnostic symbols. No opaque
   * structure layout is copied. Ordinary/compatibility paths do not use them. */
  for (int bit = 0; bit < 3; ++bit) if (value >= 100 && value < 108 && ((value - 100) & (1 << bit))) {
    void (*set)(rd_kafka_error_t *) = (void (*)(rd_kafka_error_t *))dlsym(RTLD_NEXT, flag_setter_names[bit]);
    if (!set) { fprintf(stderr, "transaction native fixture: %s\n", dlerror()); abort(); }
    set(error);
  }
  return error;
}
static int overridden(int value) { return (value >= 100 && value < 108) || value == 200 || value == 201; }

rd_kafka_error_t *rd_kafka_init_transactions(rd_kafka_t *client, int timeout) {
  REAL(rd_kafka_init_transactions); int value = current_mode();
  return track(client, overridden(value) ? (value == 201 ? NULL : fixture(value)) : real(client, timeout));
}
rd_kafka_error_t *rd_kafka_begin_transaction(rd_kafka_t *client) {
  REAL(rd_kafka_begin_transaction); int value = current_mode();
  return track(client, overridden(value) ? (value == 201 ? NULL : fixture(value)) : real(client));
}
rd_kafka_error_t *rd_kafka_abort_transaction(rd_kafka_t *client, int timeout) {
  REAL(rd_kafka_abort_transaction); int value = current_mode();
  return track(client, overridden(value) ? (value == 201 ? NULL : fixture(value)) : real(client, timeout));
}
rd_kafka_error_t *rd_kafka_commit_transaction(rd_kafka_t *client, int timeout) {
  REAL(rd_kafka_commit_transaction); int value = current_mode();
  return track(client, overridden(value) ? (value == 201 ? NULL : fixture(value)) : real(client, timeout));
}
rd_kafka_error_t *rd_kafka_send_offsets_to_transaction(rd_kafka_t *client,
    const rd_kafka_topic_partition_list_t *offsets,
    const rd_kafka_consumer_group_metadata_t *metadata, int timeout) {
  REAL(rd_kafka_send_offsets_to_transaction); int value = current_mode();
  pthread_mutex_lock(&lock);
  if (enabled && offsets && offsets->cnt == 1) counts[12] = offsets->elems[0].offset;
  pthread_mutex_unlock(&lock);
  return track(client, overridden(value) ? (value == 201 ? NULL : fixture(value)) : real(client, offsets, metadata, timeout));
}
rd_kafka_error_t *rd_kafka_sasl_set_credentials(rd_kafka_t *client,
    const char *username, const char *password) {
  REAL(rd_kafka_sasl_set_credentials); int value = current_mode();
  /* The public Haskell arguments are non-NULL. Ask the real native function
   * for its actual invalid-argument object to exercise the owned error path. */
  rd_kafka_error_t *error = track(client, value == 300 || value == 301 || value == 20 ? real(client, NULL, NULL) : real(client, username, password));
  if (value == 301) {
    pthread_mutex_lock(&lock); counts[9]++;
    while (!released) pthread_cond_wait(&resume, &lock);
    pthread_mutex_unlock(&lock);
  }
  return error;
}
rd_kafka_resp_err_t rd_kafka_error_code(const rd_kafka_error_t *error) {
  REAL(rd_kafka_error_code); reading(error, 2); return real(error);
}
int rd_kafka_error_is_fatal(const rd_kafka_error_t *error) {
  REAL(rd_kafka_error_is_fatal); reading(error, 3); return real(error);
}
int rd_kafka_error_is_retriable(const rd_kafka_error_t *error) {
  REAL(rd_kafka_error_is_retriable); reading(error, 4); return real(error);
}
int rd_kafka_error_txn_requires_abort(const rd_kafka_error_t *error) {
  REAL(rd_kafka_error_txn_requires_abort); reading(error, 5); return real(error);
}
rd_kafka_consumer_group_metadata_t *rd_kafka_consumer_group_metadata(rd_kafka_t *client) {
  REAL(rd_kafka_consumer_group_metadata);
  rd_kafka_consumer_group_metadata_t *result = real(client);
  pthread_mutex_lock(&lock); if (enabled && result) counts[6]++; pthread_mutex_unlock(&lock);
  return result;
}
void rd_kafka_consumer_group_metadata_destroy(rd_kafka_consumer_group_metadata_t *metadata) {
  REAL(rd_kafka_consumer_group_metadata_destroy);
  pthread_mutex_lock(&lock); if (enabled && metadata) counts[7]++; pthread_mutex_unlock(&lock);
  real(metadata);
}

/* Reproduce the inherited decode-without-destroy sequence on the same live,
 * nontransactional producer. The enclosing fixture later releases all three. */
void rdkafka_hs_test_tx_leak_control(void) {
  pthread_mutex_lock(&lock); rd_kafka_t *client = last_client; pthread_mutex_unlock(&lock);
  if (!client) abort();
  { REAL(rd_kafka_init_transactions); rd_kafka_error_code(track(client, real(client, 0))); }
  { REAL(rd_kafka_begin_transaction); rd_kafka_error_code(track(client, real(client))); }
  { REAL(rd_kafka_abort_transaction); rd_kafka_error_code(track(client, real(client, 0))); }
}