|  | @@ -846,25 +846,87 @@ mg_atomic_dec(volatile int *addr)
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  | -static unsigned long mg_memory_debug_blockCount = 0;
 | 
	
		
			
				|  |  | -static unsigned long mg_memory_debug_totalMemUsed = 0;
 | 
	
		
			
				|  |  | +#if defined(__GNUC__) || defined(__MINGW32__)
 | 
	
		
			
				|  |  | +/* Show no warning in case system functions are not used. */
 | 
	
		
			
				|  |  | +#pragma GCC diagnostic push
 | 
	
		
			
				|  |  | +#pragma GCC diagnostic ignored "-Wunused-function"
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +#if defined(__clang__)
 | 
	
		
			
				|  |  | +/* Show no warning in case system functions are not used. */
 | 
	
		
			
				|  |  | +#pragma clang diagnostic push
 | 
	
		
			
				|  |  | +#pragma clang diagnostic ignored "-Wunused-function"
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int
 | 
	
		
			
				|  |  | +mg_atomic_add(volatile int *addr, int value)
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +	int ret;
 | 
	
		
			
				|  |  | +#if defined(_WIN32) && !defined(__SYMBIAN32__)
 | 
	
		
			
				|  |  | +	/* Depending on the SDK, this function uses either
 | 
	
		
			
				|  |  | +	 * (volatile unsigned int *) or (volatile LONG *),
 | 
	
		
			
				|  |  | +	 * so whatever you use, the other SDK is likely to raise a warning. */
 | 
	
		
			
				|  |  | +	ret = InterlockedAdd((volatile long *)addr, (long)value);
 | 
	
		
			
				|  |  | +#elif defined(__GNUC__)                                                        \
 | 
	
		
			
				|  |  | +    && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 0)))
 | 
	
		
			
				|  |  | +	ret = __sync_add_and_fetch(addr, value);
 | 
	
		
			
				|  |  | +#else
 | 
	
		
			
				|  |  | +	ret = (++(*addr));
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +	return ret;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(__GNUC__)
 | 
	
		
			
				|  |  | +/* Show no warning in case system functions are not used. */
 | 
	
		
			
				|  |  | +#pragma GCC diagnostic pop
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +#if defined(__clang__)
 | 
	
		
			
				|  |  | +/* Show no warning in case system functions are not used. */
 | 
	
		
			
				|  |  | +#pragma clang diagnostic pop
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(USE_SERVER_STATS)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// static unsigned long mg_memory_debug_blockCount = 0;
 | 
	
		
			
				|  |  | +// static unsigned long mg_memory_debug_totalMemUsed = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +volatile int mg_memory_debug_blockCount = 0;
 | 
	
		
			
				|  |  | +volatile int mg_memory_debug_totalMemUsed = 0;
 | 
	
		
			
				|  |  | +volatile int mg_memory_debug_maxMemUsed = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void *
 | 
	
		
			
				|  |  | -mg_malloc_ex(size_t size, const char *file, unsigned line)
 | 
	
		
			
				|  |  | +mg_malloc_ex(size_t size,
 | 
	
		
			
				|  |  | +             struct mg_context *ctx,
 | 
	
		
			
				|  |  | +             const char *file,
 | 
	
		
			
				|  |  | +             unsigned line)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	void *data = malloc(size + sizeof(size_t));
 | 
	
		
			
				|  |  |  	void *memory = 0;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  	char mallocStr[256];
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	(void)ctx;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	if (data) {
 | 
	
		
			
				|  |  | +		int mmem = mg_atomic_add(&mg_memory_debug_totalMemUsed, size);
 | 
	
		
			
				|  |  | +		if (mmem > mg_memory_debug_maxMemUsed) {
 | 
	
		
			
				|  |  | +			/* could use atomic compare exchange, but this
 | 
	
		
			
				|  |  | +			 * seems overkill for statistics data */
 | 
	
		
			
				|  |  | +			mg_memory_debug_maxMemUsed = mmem;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		mg_atomic_inc(&mg_memory_debug_blockCount);
 | 
	
		
			
				|  |  |  		*(size_t *)data = size;
 | 
	
		
			
				|  |  | -		mg_memory_debug_totalMemUsed += size;
 | 
	
		
			
				|  |  | -		mg_memory_debug_blockCount++;
 | 
	
		
			
				|  |  |  		memory = (void *)(((char *)data) + sizeof(size_t));
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  	sprintf(mallocStr,
 | 
	
		
			
				|  |  |  	        "MEM: %p %5lu alloc   %7lu %4lu --- %s:%u\n",
 | 
	
		
			
				|  |  |  	        memory,
 | 
	
	
		
			
				|  | @@ -878,15 +940,23 @@ mg_malloc_ex(size_t size, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  |  	DEBUG_TRACE("%s", mallocStr);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	return memory;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void *
 | 
	
		
			
				|  |  | -mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line)
 | 
	
		
			
				|  |  | +mg_calloc_ex(size_t count,
 | 
	
		
			
				|  |  | +             size_t size,
 | 
	
		
			
				|  |  | +             struct mg_context *ctx,
 | 
	
		
			
				|  |  | +             const char *file,
 | 
	
		
			
				|  |  | +             unsigned line)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |  	void *data = mg_malloc_ex(size * count, file, line);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	(void)ctx;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	if (data) {
 | 
	
		
			
				|  |  |  		memset(data, 0, size * count);
 | 
	
		
			
				|  |  |  	}
 | 
	
	
		
			
				|  | @@ -895,16 +965,24 @@ mg_calloc_ex(size_t count, size_t size, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void
 | 
	
		
			
				|  |  | -mg_free_ex(void *memory, const char *file, unsigned line)
 | 
	
		
			
				|  |  | +mg_free_ex(void *memory,
 | 
	
		
			
				|  |  | +           struct mg_context *ctx,
 | 
	
		
			
				|  |  | +           const char *file,
 | 
	
		
			
				|  |  | +           unsigned line)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  	char mallocStr[256];
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  	void *data = (void *)(((char *)memory) - sizeof(size_t));
 | 
	
		
			
				|  |  |  	size_t size;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	(void)ctx;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	if (memory) {
 | 
	
		
			
				|  |  |  		size = *(size_t *)data;
 | 
	
		
			
				|  |  | -		mg_memory_debug_totalMemUsed -= size;
 | 
	
		
			
				|  |  | -		mg_memory_debug_blockCount--;
 | 
	
		
			
				|  |  | +		mg_atomic_add(&mg_memory_debug_totalMemUsed, -size);
 | 
	
		
			
				|  |  | +		mg_atomic_dec(&mg_memory_debug_blockCount);
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  		sprintf(mallocStr,
 | 
	
		
			
				|  |  |  		        "MEM: %p %5lu free    %7lu %4lu --- %s:%u\n",
 | 
	
		
			
				|  |  |  		        memory,
 | 
	
	
		
			
				|  | @@ -918,20 +996,28 @@ mg_free_ex(void *memory, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  |  		DEBUG_TRACE("%s", mallocStr);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  		free(data);
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void *
 | 
	
		
			
				|  |  | -mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
 | 
	
		
			
				|  |  | +mg_realloc_ex(void *memory,
 | 
	
		
			
				|  |  | +              size_t newsize,
 | 
	
		
			
				|  |  | +              struct mg_context *ctx,
 | 
	
		
			
				|  |  | +              const char *file,
 | 
	
		
			
				|  |  | +              unsigned line)
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  	char mallocStr[256];
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  	void *data;
 | 
	
		
			
				|  |  |  	void *_realloc;
 | 
	
		
			
				|  |  |  	size_t oldsize;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	(void)ctx;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	if (newsize) {
 | 
	
		
			
				|  |  |  		if (memory) {
 | 
	
		
			
				|  |  |  			data = (void *)(((char *)memory) - sizeof(size_t));
 | 
	
	
		
			
				|  | @@ -940,6 +1026,7 @@ mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  			if (_realloc) {
 | 
	
		
			
				|  |  |  				data = _realloc;
 | 
	
		
			
				|  |  |  				mg_memory_debug_totalMemUsed -= oldsize;
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  				sprintf(mallocStr,
 | 
	
		
			
				|  |  |  				        "MEM: %p %5lu r-free  %7lu %4lu --- %s:%u\n",
 | 
	
		
			
				|  |  |  				        memory,
 | 
	
	
		
			
				|  | @@ -953,7 +1040,9 @@ mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  |  				DEBUG_TRACE("%s", mallocStr);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  				mg_memory_debug_totalMemUsed += newsize;
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  				sprintf(mallocStr,
 | 
	
		
			
				|  |  |  				        "MEM: %p %5lu r-alloc %7lu %4lu --- %s:%u\n",
 | 
	
		
			
				|  |  |  				        memory,
 | 
	
	
		
			
				|  | @@ -967,14 +1056,17 @@ mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  |  				DEBUG_TRACE("%s", mallocStr);
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  				*(size_t *)data = newsize;
 | 
	
		
			
				|  |  |  				data = (void *)(((char *)data) + sizeof(size_t));
 | 
	
		
			
				|  |  |  			} else {
 | 
	
		
			
				|  |  | +#if defined(MEMORY_DEBUGGING)
 | 
	
		
			
				|  |  |  #if defined(_WIN32)
 | 
	
		
			
				|  |  |  				OutputDebugStringA("MEM: realloc failed\n");
 | 
	
		
			
				|  |  |  #else
 | 
	
		
			
				|  |  |  				DEBUG_TRACE("%s", "MEM: realloc failed\n");
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  				return _realloc;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  		} else {
 | 
	
	
		
			
				|  | @@ -988,12 +1080,17 @@ mg_realloc_ex(void *memory, size_t newsize, const char *file, unsigned line)
 | 
	
		
			
				|  |  |  	return data;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#define mg_malloc(a) mg_malloc_ex(a, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | -#define mg_calloc(a, b) mg_calloc_ex(a, b, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | -#define mg_realloc(a, b) mg_realloc_ex(a, b, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | -#define mg_free(a) mg_free_ex(a, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_malloc(a) mg_malloc_ex(a, NULL, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_calloc(a, b) mg_calloc_ex(a, b, NULL, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_realloc(a, b) mg_realloc_ex(a, b, NULL, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_free(a) mg_free_ex(a, NULL, __FILE__, __LINE__)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | +#define mg_malloc_ctx(a, c) mg_malloc_ex(a, c, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_calloc_ctx(a, b, c) mg_calloc_ex(a, b, c, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_realloc_ctx(a, b, c) mg_realloc_ex(a, b, c, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +#define mg_free_ctx(a, c) mg_free_ex(a, c, __FILE__, __LINE__)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#else /* USE_SERVER_STATS */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static __inline void *
 | 
	
		
			
				|  |  |  mg_malloc(size_t a)
 | 
	
	
		
			
				|  | @@ -1019,7 +1116,12 @@ mg_free(void *a)
 | 
	
		
			
				|  |  |  	free(a);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | +#define mg_malloc_ctx(a, c) mg_malloc(a)
 | 
	
		
			
				|  |  | +#define mg_calloc_ctx(a, b, c) mg_calloc(a, b)
 | 
	
		
			
				|  |  | +#define mg_realloc_ctx(a, b, c) mg_realloc(a, b)
 | 
	
		
			
				|  |  | +#define mg_free_ctx(a, c) mg_free(a)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#endif /* USE_SERVER_STATS */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static void mg_vsnprintf(const struct mg_connection *conn,
 | 
	
	
		
			
				|  | @@ -1979,6 +2081,13 @@ struct mg_context {
 | 
	
		
			
				|  |  |  #if defined(USE_LUA)
 | 
	
		
			
				|  |  |  	void *lua_background_state;
 | 
	
		
			
				|  |  |  #endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(USE_SERVER_STATS)
 | 
	
		
			
				|  |  | +	int active_connections;
 | 
	
		
			
				|  |  | +	int total_connections;
 | 
	
		
			
				|  |  | +	int total_requests;
 | 
	
		
			
				|  |  | +	int max_connections;
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -14173,6 +14282,16 @@ process_new_connection(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  		const char *hostend;
 | 
	
		
			
				|  |  |  		int reqerr, uri_type;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#if defined(USE_SERVER_STATS)
 | 
	
		
			
				|  |  | +		int mcon = mg_atomic_inc(&(conn->ctx->total_connections));
 | 
	
		
			
				|  |  | +		mg_atomic_inc(&(conn->ctx->active_connections));
 | 
	
		
			
				|  |  | +		if (mcon > (conn->ctx->max_connections)) {
 | 
	
		
			
				|  |  | +			/* could use atomic compare exchange, but this
 | 
	
		
			
				|  |  | +			 * seems overkill for statistics data */
 | 
	
		
			
				|  |  | +			conn->ctx->max_connections = mcon;
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		keep_alive_enabled =
 | 
	
		
			
				|  |  |  		    !strcmp(conn->ctx->config[ENABLE_KEEP_ALIVE], "yes");
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -14318,6 +14437,11 @@ process_new_connection(struct mg_connection *conn)
 | 
	
		
			
				|  |  |  			conn->handled_requests++;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		} while (keep_alive);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#if defined(USE_SERVER_STATS)
 | 
	
		
			
				|  |  | +		mg_atomic_add(&(conn->ctx->total_requests), conn->handled_requests);
 | 
	
		
			
				|  |  | +		mg_atomic_dec(&(conn->ctx->active_connections));
 | 
	
		
			
				|  |  | +#endif
 | 
	
		
			
				|  |  |  	}
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 |