Skip to content

Instantly share code, notes, and snippets.

@jmjatlanta
Last active September 3, 2018 13:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmjatlanta/63cf7a13b9c88d47c9eb63deee5e5118 to your computer and use it in GitHub Desktop.
Save jmjatlanta/63cf7a13b9c88d47c9eb63deee5e5118 to your computer and use it in GitHub Desktop.
What happens when you use an thread-unsafe collection
CC=g++
CFLAGS=-g
DEPS=
%.o: %.cpp $(DEPS)
$(CC) -c -o $@ $< $(CFLAGS)
test_threadsafe: test_threadsafe.o
$(CC) -g -O0 -lSegFault -o test_threadsafe $^ -lpthread -rdynamic
Got signal 11, faulty address is 0x8, from 0x559961bec86a
[bt] Execution path:
[bt] ./test_threadsafe(_ZNKSt12__shared_ptrI8MyObjectLN9__gnu_cxx12_Lock_policyE2EE3getEv+0xc) [0x559961bec86a]
[bt] ./test_threadsafe(_ZNKSt12__shared_ptrI8MyObjectLN9__gnu_cxx12_Lock_policyE2EE3getEv+0xc) [0x559961bec86a]
[bt] ./test_threadsafe(_ZSteqI8MyObjectS0_EbRKSt10shared_ptrIT_ERKS1_IT0_E+0x2c) [0x559961beeb80]
[bt] ./test_threadsafe(_ZNKSt8equal_toISt10shared_ptrI8MyObjectEEclERKS2_S5_+0x27) [0x559961bee8a9]
[bt] ./test_threadsafe(_ZNSt8__detail13_Equal_helperISt10shared_ptrI8MyObjectES3_NS_9_IdentityESt8equal_toIS3_EmLb0EE9_S_equalsERKS6_RKS4_RKS3_mPNS_10_Hash_nodeIS3_Lb0EEE+0x50) [0x559961bee5b6]
[bt] ./test_threadsafe(_ZNKSt8__detail15_Hashtable_baseISt10shared_ptrI8MyObjectES3_NS_9_IdentityESt8equal_toIS3_ESt4hashIS3_ENS_18_Mod_range_hashingENS_20_Default_ranged_hashENS_17_Hashtable_traitsILb0ELb1ELb1EEEE9_M_equalsERKS3_mPNS_10_Hash_nodeIS3_Lb0EEE+0x54) [0x559961bedf24]
[bt] ./test_threadsafe(_ZNKSt10_HashtableISt10shared_ptrI8MyObjectES2_SaIS2_ENSt8__detail9_IdentityESt8equal_toIS2_ESt4hashIS2_ENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb0ELb1ELb1EEEE19_M_find_before_nodeEmRKS2_m+0x65) [0x559961bed663]
[bt] ./test_threadsafe(_ZNKSt10_HashtableISt10shared_ptrI8MyObjectES2_SaIS2_ENSt8__detail9_IdentityESt8equal_toIS2_ESt4hashIS2_ENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb0ELb1ELb1EEEE12_M_find_nodeEmRKS2_m+0x30) [0x559961becb3a]
[bt] ./test_threadsafe(_ZNSt10_HashtableISt10shared_ptrI8MyObjectES2_SaIS2_ENSt8__detail9_IdentityESt8equal_toIS2_ESt4hashIS2_ENS4_18_Mod_range_hashingENS4_20_Default_ranged_hashENS4_20_Prime_rehash_policyENS4_17_Hashtable_traitsILb0ELb1ELb1EEEE4findERKS2_+0x69) [0x559961bec42f]
[bt] ./test_threadsafe(_ZNSt13unordered_setISt10shared_ptrI8MyObjectESt4hashIS2_ESt8equal_toIS2_ESaIS2_EE4findERKS2_+0x23) [0x559961bebc05]
[bt] ./test_threadsafe(_Z9find_loopv+0x3a) [0x559961beaea0]
[bt] ./test_threadsafe(_ZSt13__invoke_implIvPFvvEJEET_St14__invoke_otherOT0_DpOT1_+0x1d) [0x559961bec653]
[bt] ./test_threadsafe(_ZSt8__invokeIPFvvEJEENSt15__invoke_resultIT_JDpT0_EE4typeEOS3_DpOS4_+0x35) [0x559961bebccc]
[bt] ./test_threadsafe(_ZNSt6thread8_InvokerISt5tupleIJPFvvEEEE9_M_invokeIJLm0EEEEDTcl8__invokespcl10_S_declvalIXT_EEEEESt12_Index_tupleIJXspT_EEE+0x28) [0x559961beef5a]
[bt] ./test_threadsafe(_ZNSt6thread8_InvokerISt5tupleIJPFvvEEEEclEv+0x2c) [0x559961beeec8]
#include <unordered_set>
#include <string>
#include <thread>
#include <iostream>
#include <memory>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
char* exe = 0;
int initialiseExecutableName()
{
char link[1024];
exe = new char[1024];
snprintf(link,sizeof link,"/proc/%d/exe",getpid());
if(readlink(link,exe,sizeof link)==-1) {
fprintf(stderr,"ERRORRRRR\n");
exit(1);
}
printf("Executable name initialised: %s\n",exe);
}
const char* getExecutableName()
{
if (exe == 0)
initialiseExecutableName();
return exe;
}
/* get REG_EIP from ucontext.h */
#define __USE_GNU
#include <ucontext.h>
void bt_sighandler(int sig, siginfo_t *info,
void *secret) {
void *trace[16];
char **messages = (char **)nullptr;
int i, trace_size = 0;
ucontext_t *uc = (ucontext_t *)secret;
/* Do something useful with siginfo_t */
if (sig == SIGSEGV)
fprintf(stderr, "Got signal %d, faulty address is %p, "
"from %p\n", sig, info->si_addr,
uc->uc_mcontext.gregs[REG_RIP]);
else
fprintf(stderr, "Got signal %d\n", sig);
trace_size = backtrace(trace, 16);
/* overwrite sigaction with caller's address */
trace[1] = (void *) uc->uc_mcontext.gregs[REG_RIP];
messages = backtrace_symbols(trace, trace_size);
/* skip first stack frame (points here) */
fprintf(stderr, "[bt] Execution path:\n");
for (i=1; i<trace_size; ++i)
{
fprintf(stderr, "[bt] %s\n", messages[i]);
/* find first occurence of '(' or ' ' in message[i] and assume
* everything before that is the file name. (Don't go beyond 0 though
* (string terminator)*/
size_t p = 0;
while(messages[i][p] != '(' && messages[i][p] != ' '
&& messages[i][p] != 0)
++p;
char syscom[256];
sprintf(syscom,"addr2line %p -e %.*s", trace[i] , p, messages[i] );
//last parameter is the filename of the symbol
system(syscom);
}
exit(0);
}
class MyObject
{
public:
int id;
std::string name;
};
std::unordered_set<std::shared_ptr<MyObject>> collection;
std::shared_ptr<MyObject> main_object = nullptr;
void add_loop()
{
while (true)
{
std::shared_ptr<MyObject> obj = std::make_shared<MyObject>();
obj->id = rand() % 1000;
obj->name = "Hello, World!";
collection.emplace(obj);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void delete_loop()
{
while (true)
{
if (collection.size() > 0)
{
collection.erase(collection.begin());
std::this_thread::sleep_for(std::chrono::milliseconds(600));
}
}
}
void find_loop()
{
while(true)
{
bool found = collection.find(main_object) != collection.end();
if (found)
std::cout << "Found main_object\n";
else
std::cout << "main_object not found.\n";
std::this_thread::sleep_for(std::chrono::milliseconds());
}
}
/******
* Attempt to modify a collection while another
* thread does a find
*/
int main(int argc, char** argv)
{
struct sigaction sa;
sa.sa_sigaction = bt_sighandler;
sigemptyset (&sa.sa_mask);
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sigaction(SIGSEGV, &sa, NULL);
sigaction(SIGUSR1, &sa, NULL);
main_object = std::make_shared<MyObject>();
main_object->id = 1000;
main_object->name = "Main Object";
collection.emplace(main_object);
std::thread find_thread(find_loop);
std::thread add_thread(add_loop);
std::thread delete_thread(delete_loop);
// run indefinitely
while (true)
{
std::this_thread::sleep_for(std::chrono::seconds(10));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment