[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: [BUG] lua 5.2 luai_hashnum bug with threads
- From: Andrei <mrpuzzler@...>
- Date: Tue, 22 Apr 2014 11:58:42 +0400
//////////////////////////////////////////////////////////////////////
// filename: luaHashTest.cpp
// author: James Poag for Rumbic Studios
//
// purpose: This is to demonstrate a real-world scenario in which
// 'luai_hashnum' FAILS to generate a DETERMINISTIC
// hash value for identical keys when executed non-concurrently
// on separate threads.
//
// It is the fault of different precision controls being
// set on different threads, one of which is automatically
// set to 24 bits when using DirectX without the D3DCREATE_FPU_PRESERVE
// flag. (But not strictly limited to DX, anyone wanting to
// increase floating point performance with the sacrifice of
// precision).
//
// Another scenario is where the LuaVM is created and setup
// before DirectX (or anyone) resets the precision control and the
// resulting LuaVM appears empty (because of hash misses)
/////////////////////////////////////////////////////////////////////
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <float.h>
#include <math.h>
#include <limits.h>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#pragma fenv_access (on)
/*
================================================================================================
Minimum Lua defined macros to reproduce error
================================================================================================
*/
/* type of numbers in Lua */
#define LUA_NUMBER double
typedef LUA_NUMBER lua_Number;
#define cast(t, exp) ((t)(exp))
#define cast_num(i) cast(lua_Number, (i))
/*
** lua_number2int is a macro to convert lua_Number to int.
*/
#define lua_number2int(i,n) __asm {__asm fld n __asm fistp i}
#define l_mathop(x) (x)
/*
** luai_hashnum is a macro to hash a lua_Number value into an integer.
** The hash must be deterministic and give reasonable values for
** both small and large values (outside the range of integers).
*/
#define luai_hashnum(i,n) { int e; \
n = l_mathop(frexp)(n, &e) * (lua_Number)(INT_MAX - DBL_MAX_EXP); \
lua_number2int(i, n); i += e; }
/*
================================================================================================
Test Code to reproduce error
================================================================================================
*/
DWORD WINAPI workerThreadFunc (LPVOID lpThread); // forward decl
#define THREAD_COUNT 1
/*
========================
Main Entry Point
========================
*/
int _tmain(int argc, _TCHAR* argv[])
{
/*
* In this scenario, we setup both the Render System and the LuaVM
* on the [Main Thread].
*/
//////////////////////////////////////////////////////////////////////////
// DirectX sets precision to 24 bits on '[Main Thread]'
unsigned int control_word_x87;
control_word_x87 = __control87_2(_PC_24, MCW_PC,
&control_word_x87, 0);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// LuaVM is created and filled
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Per Doom3 and many modern multithreaded renderers, the [Main Thread] is
// used to:
// 1. Process User/App input events (uses LuaVM on [Main Thread])
//
// 2. Spawn/Signal [Worker Thread] that will update game and generate
// render commands for next frame (uses LuaVM on [Worker Thread])
//
// 3. Execute render commands from previous frame on [Main Thread]
// (does not use LuaVM)
//
// 4. WaitFor/Join the threads together
//
// Although the LuaVM is used on separate threads, it is never used
// concurrently.
//////////////////////////////////////////////////////////////////////////
// spawn [Worker Thread]
HANDLE pThreads [THREAD_COUNT] = {0};
for (int i = 0; i < THREAD_COUNT; ++i){
DWORD nThreadId = 0;
pThreads[i] = CreateThread (NULL,
0,
workerThreadFunc,
0,
0,
&nThreadId);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Run the hash function on the [Main Thread]
lua_Number nk = cast_num(3);
int hashVal = 0;
luai_hashnum(hashVal, nk);
printf("[Main Thread] reports: %i \n", hashVal); // 1610611970
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Waitfor/Join
WaitForMultipleObjects(THREAD_COUNT, pThreads, TRUE, INFINITE);
//////////////////////////////////////////////////////////////////////////
return 0;
}
/*
========================
[Worker Thread]
========================
*/
DWORD WINAPI workerThreadFunc (LPVOID lpThread)
{
//////////////////////////////////////////////////////////////////////////
// The precision is not set on the [Worker Thread]
//////////////////////////////////////////////////////////////////////////
Sleep(3); // avoid printf() collision & demo non-concurrent
//////////////////////////////////////////////////////////////////////////
// Run the hash function on the [Worker Thread]
lua_Number nk = cast_num(3);
int hashVal = 0;
luai_hashnum(hashVal, nk);
printf("[Worker Thread] reports: %i \n", hashVal); // 1610611969
//////////////////////////////////////////////////////////////////////////
return 0;
}