duplicated static c++ singleton instances

2,329 views
Skip to first unread message

Rasmus Brandt Olesen

unread,
May 3, 2018, 4:06:44 PM5/3/18
to android-ndk
Hi,

I have a Unity, that uses a c++ library, that is again connected to a java library.

So the call flow is:
1. Unity call c++ library
2. c++ library calls Java method using JNI
3. java method call same c++ library

In the c++ library we have a static singleton instance.
When we run this on Android 6.0+, then we get the exact same instance of that singleton, and everything is fine.

But on Android 5.0/5.1 we get 2 different instances

Can anyone help to explain why we see this behaviour on Android 5?

Thanks!,

Rasmus Brandt Olesen

Rasmus Brandt Olesen

unread,
May 17, 2018, 4:52:50 PM5/17/18
to android-ndk
We have now found a workaround for the problem. Our singleton implementation was template-based. By directly implementing the singleton stuff inside each singleton class, each singleton is only created once when running Android 5.

So it looks like templates with static variables on Android 5, is a NO-GO.

Anyone know why this is the case?
We been using NDK r16 for this.

Below is the template-based singleton implementation:

#pragma once

template<typename T>
class Singleton
{
public:
static std::shared_ptr<T> Instance()
{
if (_instance == nullptr)
{
_instance = std::make_shared<T>();
}
return _instance;
}

static void SetInstance(std::shared_ptr<T> instance)
{
_instance = instance;
}

private:
static std::shared_ptr<T> _instance;
};

template<typename T>
std::shared_ptr<T> Singleton<T>::_instance = nullptr;

Ryan Prichard

unread,
May 17, 2018, 6:20:11 PM5/17/18
to andro...@googlegroups.com
The Singleton<T>::_instance variables have "vague linkage" -- the compiler outputs a copy everywhere the variable is used and then the (build-time) linker discards duplicates. If your app has multiple shared libraries using a single Singleton<T>::_instance variable, then you would have multiple copies of the variable at run-time. The dynamic loader in Android 6 and up is better at using a single copy of a variable when it's referenced from multiple shared libraries.

A couple things to try:

 * You can pass the address of a Singleton<T>::_instance to dladdr to find the filename of the shared library where the instance is located. You'd want the dli_fname field of the Dl_info struct. http://man7.org/linux/man-pages/man3/dladdr.3.html

 * Use readelf with --dyn-syms to scan symbol tables of any of your application's shared libraries looking for copies of a Singleton<T>::_instance.

It's possible to control where template class members are emitted using extern template declarations. e.g.:

In a header file:

#include "Singleton.h"
struct MyClass { ... }; 
extern template class Singleton<MyClass>;

In a single cpp file, linked into a single shared object:

#include "Singleton.h"
#include "MyClass.h"
template class Singleton<MyClass>;

This code would emit a single copy of Singleton<MyClass>::_instance into the cpp file's object file, and any other object file would reference that copy.

-Ryan


--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at https://groups.google.com/group/android-ndk.
To view this discussion on the web visit https://groups.google.com/d/msgid/android-ndk/ac428c5e-b062-4503-b994-ea4451c32251%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Steven Winston

unread,
May 18, 2018, 12:12:10 PM5/18/18
to android-ndk
Fwiw: just incase you are using more than one thread, you might give a double lock a try.
Your code doesn't look like it's doing any locking so you're probably not calling from separate threads but worth mentioning.

kacper.k...@vestiacom.com

unread,
May 21, 2018, 10:55:01 AM5/21/18
to android-ndk
It's C++11 already - you don't need double locking. Static instances are created in a threadsafe manner.

Steven Winston

unread,
May 21, 2018, 2:20:49 PM5/21/18
to android-ndk
Thanks, this is absolutely correct for compilers that 100% support the C++ standard as 6.7.4 does state:
If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.

However, reality isn't always 100% compliant with standards for older compilers.  It's worth mentioning that VS didn't get full support until 2015 (Magic Statics in this listed table: https://msdn.microsoft.com/en-us/library/hh567368.aspx ).  In GCC the first version to get support was 4.3: https://gcc.gnu.org/projects/cxx-status.html  Which means that the first version of the NDK to receive support would probably have been Revision 3 from 2010 according to the revision history as that's when gcc went to 4.4 and every version of clang that's been in the ndk should have had it.

Given that lollipop is newer, it's not possible that the compiler doesn't support the feature. And I learned something cool about the language standard so thanks for the education! :)
Reply all
Reply to author
Forward
0 new messages