# HTTP/2 JSON Threading Issues: Debugging Guide

## Overview

This document captures critical lessons learned from debugging intermittent crashes in the Axis2/C HTTP/2 JSON service. These crashes manifested as:
- `Fatal glibc error: pthread_mutex_lock.c:450 assertion failed: e != ESRCH || !robust`
- Segmentation faults occurring after several successful requests (typically 3-7 requests)

**Key Insight**: These crashes were NOT in the application logic but in **thread synchronization infrastructure** - specifically race conditions during mutex initialization.

## Request Processing Flow with Threading Context

Building on the existing "Request Processing Flow" documentation, this section adds the **threading dimension** critical for understanding intermittent crashes.

### Complete Code Execution Path (with checkpoint locations)

```
HTTP/2 Request Arrives
        |
        v
+---------------------------------------------------------------------+
| Phase 1: Apache HTTP/2 Reception                                     |
| File: apache2_worker.c                                               |
| Function: axis2_apache2_worker_process_request()                     |
| CHECKPOINT 1: Content-type check for JSON processing                 |
+---------------------------------------------------------------------+
        |
        v
+---------------------------------------------------------------------+
| Phase 2: JSON Processor Interface Processing                         |
| File: axis2_apache2_request_processor_json_impl.c                    |
| Function: process_request_body()                                     |
| - Reads JSON from HTTP/2 stream                                      |
| - Stores in JSON_REQUEST_BODY property                               |
+---------------------------------------------------------------------+
        |
        v
+---------------------------------------------------------------------+
| Phase 3: HTTP Transport Utils Processing                             |
| File: http_transport_utils.c                                         |
| Function: axis2_http_transport_utils_process_http_post_request()     |
| CHECKPOINT A: After JSON/XML processing, shows do_rest & soap_envelope|
| CHECKPOINT A2: About to set soap envelope on msg_ctx                 |
| CHECKPOINT A3: Creating engine <- CRASH ZONE BEGINS HERE             |
+---------------------------------------------------------------------+
        |
        v
+---------------------------------------------------------------------+
| Phase 4: Engine Creation <- CRITICAL THREADING ZONE                  |
| File: engine.c                                                       |
| Function: axis2_engine_create()                                      |
|                                                                      |
| [ENGINE_CREATE] Step 1 - Allocating engine                           |
| [ENGINE_CREATE] Step 2 - Engine allocated: 0x...                     |
| [ENGINE_CREATE] Step 3 - About to create service provider            |
|         |                                                            |
|         v                                                            |
| +-------------------------------------------------------------------+|
| | axis2_engine_service_provider_create(env)                         ||
| | -> Creates new service provider instance                          ||
| +-------------------------------------------------------------------+|
|         |                                                            |
|         v                                                            |
| [ENGINE_CREATE] Step 4 - Service provider: 0x...                     |
|         |                                                            |
|         v                                                            |
| +-------------------------------------------------------------------+|
| | axis2_http_service_provider_set_impl() <- RACE CONDITION WAS HERE ||
| | File: axis2_http_service_provider.c                               ||
| |                                                                   ||
| | BEFORE FIX (Dynamic initialization - DANGEROUS):                  ||
| |   if (!g_registry_initialized) {        <- Thread A checks        ||
| |       g_provider_mutex = create_mutex() <- Thread B also checks   ||
| |       g_registry_initialized = TRUE     <- Race condition!        ||
| |   }                                                               ||
| |   mutex_lock(g_provider_mutex)          <- Corrupted mutex!       ||
| |                                                                   ||
| | AFTER FIX (Static initialization - SAFE):                         ||
| |   static pthread_mutex_t g_provider_mutex = PTHREAD_MUTEX_INIT;   ||
| |   pthread_mutex_lock(&g_provider_mutex) <- Always valid           ||
| +-------------------------------------------------------------------+|
|         |                                                            |
|         v                                                            |
| [ENGINE_CREATE] Step 5 - Service provider impl set                   |
| [ENGINE_CREATE] Step 6 - Returning engine                            |
+---------------------------------------------------------------------+
        |
        v
+---------------------------------------------------------------------+
| Phase 5: Engine Receive & Service Execution                          |
| CHECKPOINT A4: Engine created: 0x...                                 |
| CHECKPOINT A5: Got soap_body: 0x...                                  |
| CHECKPOINT 6: Checking HTTP2_JSON_ERROR_HANDLING property            |
|                                                                      |
| -> axis2_engine_receive() -> JsonRpcMessageReceiver -> Service       |
+---------------------------------------------------------------------+
        |
        v
+---------------------------------------------------------------------+
| Phase 6: Response Delivery                                           |
| [JSON RPC MSG RECV] SUCCESSFUL COMPLETION                            |
| [HTTP_TRANSPORT_UTILS] Engine receive completed - status: 1          |
| [APACHE2_WORKER_SKIP] Skipping body - response already sent          |
+---------------------------------------------------------------------+
```

## The Race Condition Explained

### Why Crashes Were Intermittent

The crash only occurred when:
1. **Multiple Apache worker threads** processed requests simultaneously
2. **Both threads** reached `axis2_http_service_provider_set_impl()` before either completed initialization
3. **Thread timing** had to be "just right" for corruption to occur

This explains why:
- First few requests succeeded (single thread, no race)
- Crashes occurred after 3-7 requests (enough concurrency to trigger race)
- Crashes were non-deterministic (timing-dependent)

### The Dangerous Pattern (BEFORE FIX)

```c
/* axis2_http_service_provider.c - DANGEROUS CODE */
static axis2_http_service_provider_t* g_service_provider = NULL;
static axutil_thread_mutex_t* g_provider_mutex = NULL;  /* Dynamically initialized */
static axis2_bool_t g_registry_initialized = AXIS2_FALSE;

static void axis2_http_service_provider_init_registry(const axutil_env_t* env)
{
    if (!g_registry_initialized) {              /* Thread A: checks, sees FALSE */
                                                /* Thread B: checks, sees FALSE */
        g_provider_mutex = axutil_thread_mutex_create(env->allocator, ...);
                                                /* Thread A: creates mutex at 0x1000 */
                                                /* Thread B: creates mutex at 0x2000, overwrites! */
        g_registry_initialized = AXIS2_TRUE;    /* Thread A: sets TRUE */
                                                /* Thread B: sets TRUE again */
    }
}

void axis2_http_service_provider_set_impl(...)
{
    axis2_http_service_provider_init_registry(env);

    if (g_provider_mutex) {
        axutil_thread_mutex_lock(g_provider_mutex);  /* Thread A: locks 0x2000 */
                                                     /* But Thread A created 0x1000! */
                                                     /* Undefined behavior -> CRASH */
        g_service_provider = provider;
        axutil_thread_mutex_unlock(g_provider_mutex);
    }
}
```

### The Safe Pattern (AFTER FIX)

```c
/* axis2_http_service_provider.c - SAFE CODE */
static axis2_http_service_provider_t* g_service_provider = NULL;
static pthread_mutex_t g_provider_mutex = PTHREAD_MUTEX_INITIALIZER;  /* Static init */

/* No initialization function needed - mutex is valid from program start */

void axis2_http_service_provider_set_impl(...)
{
    pthread_mutex_lock(&g_provider_mutex);    /* Always valid, no race possible */
    g_service_provider = provider;
    pthread_mutex_unlock(&g_provider_mutex);
}
```

## Debugging Strategy: How to Find Threading Issues

### Step 1: Add Checkpoint Logging

When crashes are intermittent, add checkpoints to narrow down the crash location:

```c
AXIS2_LOG_ERROR(env->log, AXIS2_LOG_SI,
    "[COMPONENT] CHECKPOINT X - Description, ptr=%p", (void*)pointer);
```

**Key principle**: The last checkpoint that appears in logs before a crash tells you where to look.

### Step 2: Analyze Log Patterns

Look for these patterns in `/usr/local/apache2/logs/error_log`:

```bash
# Find the crash point
cat error_log | grep -E "(CHECKPOINT|Segmentation|Fatal)" | tail -30

# Expected output showing crash location:
# [CHECKPOINT A3] Creating engine, conf_ctx=0x7a047f800170
# Fatal glibc error: pthread_mutex_lock.c:450...
# ^ Crash is INSIDE engine creation, after A3 checkpoint
```

### Step 3: Identify Threading Patterns

Look for these red flags in code:

| Pattern | Risk | Solution |
|---------|------|----------|
| `if (!initialized) { mutex = create(); initialized = true; }` | **CRITICAL** | Use `PTHREAD_MUTEX_INITIALIZER` |
| `static` variables modified by multiple threads | **HIGH** | Add mutex protection |
| Dynamic mutex creation in frequently-called functions | **HIGH** | Use static initialization |
| `axutil_thread_mutex_create()` without `pthread_once` | **MEDIUM** | Consider static init or `pthread_once` |

### Step 4: Verify Fix Deployment

```bash
# Check library has the fix
strings /usr/local/axis2c/lib/libaxis2_http_util.so.0.0.0 | grep "PTHREAD_MUTEX_INIT"

# Check library timestamp matches build
ls -la /usr/local/axis2c/lib/libaxis2_http_util.so*
ls -la /home/robert/repos/axis-axis2-c-core/src/core/transport/http/util/.libs/libaxis2_http_util.so*

# CRITICAL: Restart Apache after deploying
sudo /usr/local/apache2/bin/apachectl -k restart
```

## Quick Diagnostic Commands

### For Intermittent Crashes

```bash
# 1. Run stress test
seq 1 100 | xargs -I{} curl -sk --http2 \
  -H "Content-Type: application/json" \
  -d '{"n_assets": 100}' \
  https://localhost/services/FinancialBenchmarkService/generateTestData \
  -w "%{http_code}\n" -o /dev/null 2>&1 | sort | uniq -c

# 2. Check for crashes during test
cat /usr/local/apache2/logs/error_log | grep -E "(Segmentation|Fatal|pthread)" | tail -10

# 3. Find last checkpoint before crash
cat /usr/local/apache2/logs/error_log | grep "CHECKPOINT" | tail -20
```

### For Mutex-Related Crashes

```bash
# Look for glibc mutex assertions
cat /usr/local/apache2/logs/error_log | grep -i "pthread_mutex\|ESRCH\|robust"

# Check if robust mutex is causing issues
nm -D /usr/local/axis2c/lib/libaxutil.so.0 | grep mutex

# Expected (safe): pthread_mutex_lock, pthread_mutex_unlock
# Problematic: pthread_mutex_consistent, pthread_mutexattr_setrobust
```

## Files Modified in This Fix

| File | Change | Purpose |
|------|--------|---------|
| `src/core/transport/http/util/axis2_http_service_provider.c` | Replaced dynamic mutex init with `PTHREAD_MUTEX_INITIALIZER` | Eliminate race condition |
| `util/src/platforms/unix/thread_unix.c` | Removed robust mutex attributes | Prevent glibc assertions |
| `src/core/transport/http/util/http_transport_utils.c` | Added checkpoint logging | Debug visibility |
| `src/core/engine/engine.c` | Added checkpoint logging | Debug visibility |

## Key Lessons for Future Debugging

1. **Intermittent crashes in multi-threaded code are almost always race conditions**
2. **Dynamic initialization of synchronization primitives is dangerous** - prefer static initialization
3. **Checkpoint logging is essential** - add it liberally during debugging, can remove later
4. **Always verify library deployment** - Apache caches modules, must restart
5. **The crash location in logs points to the symptom, not the cause** - trace back through the code path
6. **100-request stress tests are more reliable than manual testing** for finding race conditions

## Related Documentation

- `HTTP2_RESPONSE.md` - Request Processing Flow overview
- `HTTP2_JSON_REQUEST_CODE_PATH.md` - Detailed code path analysis
- `HTTP2_SERVICE_PROVIDER_INTERFACE_PATTERN.md` - Service provider architecture
- `HTTP2_APACHE2_WORKER.md` - Apache worker threading model

---

*Document based on debugging session December 30, 2025*
*Root cause: Race condition in service provider mutex initialization*
*Resolution: Static mutex initialization with PTHREAD_MUTEX_INITIALIZER*
