Initial work

This commit is contained in:
Simon Fels 2016-05-26 22:38:57 +02:00 • committed by Simon Fels
commit 3222551702
374 changed files with 40290 additions and 0 deletions

View file

@ -0,0 +1,2 @@
add_subdirectory(emugl)
add_subdirectory(OpenglCodecCommon)

View file

@ -0,0 +1,24 @@
# This build script corresponds to a library containing many definitions
# common to both the guest and the host. They relate to
#
LOCAL_PATH := $(call my-dir)
commonSources := \
glUtils.cpp \
ChecksumCalculator.cpp \
ChecksumCalculatorThreadInfo.cpp \
host_commonSources := $(commonSources)
host_commonLdLibs := $(CXX_STD_LIB)
### OpenglCodecCommon host ##############################################
$(call emugl-begin-host-static-library,libOpenglCodecCommon)
LOCAL_SRC_FILES := $(host_commonSources)
$(call emugl-import, libemugl_common)
$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/host/include/libOpenglRender $(LOCAL_PATH))
$(call emugl-export,LDLIBS,$(host_commonLdLibs))
$(call emugl-end-module)

View file

@ -0,0 +1,6 @@
set(SOURCES
glUtils.cpp
ChecksumCalculator.cpp
ChecksumCalculatorThreadInfo.cpp)
add_library(OpenglCodecCommon ${SOURCES})

View file

@ -0,0 +1,154 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ChecksumCalculator.h"
#include <string>
#include <vector>
#include <string.h>
// Checklist when implementing new protocol:
// 1. update CHECKSUMHELPER_MAX_VERSION
// 2. update maxChecksumSize()
// 3. update checksumByteSize()
// 4. update addBuffer, writeChecksum, resetChecksum, validate
// change CHECKSUMHELPER_MAX_VERSION when you want to update the protocol version
#define CHECKSUMHELPER_MAX_VERSION 1
// checksum buffer size
// Please add a new checksum buffer size when implementing a new protocol,
// as well as modifying the maxChecksumSize function.
static const size_t kV1ChecksumSize = 8;
static constexpr size_t maxChecksumSize() {
return 0 > kV1ChecksumSize ? 0 : kV1ChecksumSize;
}
static const size_t kMaxChecksumSize = maxChecksumSize();
// utility macros to create checksum string at compilation time
#define CHECKSUMHELPER_VERSION_STR_PREFIX "ANDROID_EMU_CHECKSUM_HELPER_v"
#define CHECKSUMHELPER_MACRO_TO_STR(x) #x
#define CHECKSUMHELPER_MACRO_VAL_TO_STR(x) CHECKSUMHELPER_MACRO_TO_STR(x)
static const uint32_t kMaxVersion = CHECKSUMHELPER_MAX_VERSION;
static const char* kMaxVersionStrPrefix = CHECKSUMHELPER_VERSION_STR_PREFIX;
static const char* kMaxVersionStr = CHECKSUMHELPER_VERSION_STR_PREFIX CHECKSUMHELPER_MACRO_VAL_TO_STR(CHECKSUMHELPER_MAX_VERSION);
#undef CHECKSUMHELPER_MAX_VERSION
#undef CHECKSUMHELPER_VERSION_STR_PREFIX
#undef CHECKSUMHELPER_MACRO_TO_STR
#undef CHECKSUMHELPER_MACRO_VAL_TO_STR
uint32_t ChecksumCalculator::getMaxVersion() {return kMaxVersion;}
const char* ChecksumCalculator::getMaxVersionStr() {return kMaxVersionStr;}
const char* ChecksumCalculator::getMaxVersionStrPrefix() {return kMaxVersionStrPrefix;}
bool ChecksumCalculator::setVersion(uint32_t version) {
if (version > kMaxVersion) { // unsupported version
LOG_CHECKSUMHELPER("%s: ChecksumCalculator Set Unsupported version Version %d\n",
__FUNCTION__, m_version);
return false;
}
if (m_isEncodingChecksum) { // setVersion is called in the middle of encoding checksums
LOG_CHECKSUMHELPER("%s: called between addBuffer and writeChecksum\n",
__FUNCTION__);
return false;
}
m_version = version;
LOG_CHECKSUMHELPER("%s: ChecksumCalculator Set Version %d\n", __FUNCTION__,
m_version);
return true;
}
size_t ChecksumCalculator::checksumByteSize() const {
switch (m_version) {
case 0:
return 0;
case 1:
return sizeof(uint32_t) + sizeof(m_numWrite);
default:
return 0;
}
}
void ChecksumCalculator::addBuffer(const void* buf, size_t packetLen) {
m_isEncodingChecksum = true;
switch (m_version) {
case 1:
m_v1BufferTotalLength += packetLen;
break;
}
}
bool ChecksumCalculator::writeChecksum(void* outputChecksum, size_t outputChecksumLen) {
if (outputChecksumLen < checksumByteSize()) return false;
char *checksumPtr = (char *)outputChecksum;
switch (m_version) {
case 1: { // protocol v1 is to reverse the packetLen and write it at the end
uint32_t val = computeV1Checksum();
memcpy(checksumPtr, &val, sizeof(val));
memcpy(checksumPtr+sizeof(val), &m_numWrite, sizeof(m_numWrite));
break;
}
}
resetChecksum();
m_numWrite++;
return true;
}
void ChecksumCalculator::resetChecksum() {
switch (m_version) {
case 1:
m_v1BufferTotalLength = 0;
break;
}
m_isEncodingChecksum = false;
}
bool ChecksumCalculator::validate(const void* expectedChecksum, size_t expectedChecksumLen) {
size_t checksumSize = checksumByteSize();
if (expectedChecksumLen != checksumSize) {
m_numRead++;
resetChecksum();
return false;
}
// buffers for computing the checksum
unsigned char sChecksumBuffer[kMaxChecksumSize];
switch (m_version) {
case 1: {
uint32_t val = computeV1Checksum();
memcpy(sChecksumBuffer, &val, sizeof(val));
memcpy(sChecksumBuffer+sizeof(val), &m_numRead, sizeof(m_numRead));
break;
}
}
bool isValid = !memcmp(sChecksumBuffer, expectedChecksum, checksumSize);
m_numRead++;
resetChecksum();
return isValid;
}
uint32_t ChecksumCalculator::computeV1Checksum() {
uint32_t revLen = m_v1BufferTotalLength;
revLen = (revLen & 0xffff0000) >> 16 | (revLen & 0x0000ffff) << 16;
revLen = (revLen & 0xff00ff00) >> 8 | (revLen & 0x00ff00ff) << 8;
revLen = (revLen & 0xf0f0f0f0) >> 4 | (revLen & 0x0f0f0f0f) << 4;
revLen = (revLen & 0xcccccccc) >> 2 | (revLen & 0x33333333) << 2;
revLen = (revLen & 0xaaaaaaaa) >> 1 | (revLen & 0x55555555) << 1;
return revLen;
}

View file

@ -0,0 +1,181 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stdint.h>
#include <stdlib.h>
// Set TRACE_CHECKSUMHELPER to 1 to debug creation/destruction of GLprotocol
// instances.
#define TRACE_CHECKSUMHELPER 0
#if TRACE_CHECKSUMHELPER
#define LOG_CHECKSUMHELPER(x...) fprintf(stderr, x)
#else
#define LOG_CHECKSUMHELPER(x...)
#endif
// ChecksumCalculator adds checksum as an array of bytes to GL pipe communication, which
// size depends on the protocol version. Each pipe should use one ChecksumCalculator.
// It can:
// (1) take a list of buffers one by one and compute their checksum string,
// in this case the checksum should be as the data in those buffers are
// concatenated;
// (2) compute the checksum of the buffer list, then either write them into
// a buffer provided by user, or compare it against a checksum provided
// by user
// (3) support different checksum version in future.
//
// For backward compatibility, checksum version 0 behaves the same as there is
// no checksum (i.e., checksumByteSize returns 0, validate always returns true,
// addBuffer and writeCheckSum does nothing).
//
// Notice that to detect package lost, ChecksumCalculator also keeps track of how
// many times it generates/validates checksums, and might use it as part of the
// checksum.
//
// To evaluate checksums from a list of data buffers buf1, buf2... Please call
// addBuffer(buf1, buf1len), addBuffer(buf2, buf2len) ... in order.
// Then if the checksum needs to be encoded into a buffer, one needs to allocate
// a checksum buffer with size checksumByteSize(), and call
// writeChecksum(checksumBuffer) to write the checksum to the buffer.
// If the checksum needs to be validated against an existing one, one needs to
// call validate(existChecksum, existChecksumLen).
//
// The checksum generator and validator must be set to the same version, and
// the validator must check ALL checksums in the order they are generated,
// otherwise the validation function will return false.
//
// It is allowed to change the checksum version between calculating two
// checksums. This is designed for backward compatibility reason.
//
// Example 1, encoding and decoding:
//
// bool testChecksum(void* buf, size_t bufLen) {
// // encoding message
// ChecksumCalculator encoder;
// encoder.setVersion(1);
// encoder.addBuffer(buf, bufLen);
// std::vector<unsigned char> message(bufLen + encoder.checksumByteSize());
// memcpy(&message[0], buf, bufLen);
// encoder.writeChecksum(&message[0] + bufLen, encoder.checksumByteSize());
//
// // decoding message
// ChecksumCalculator decoder;
// decoder.setVersion(1);
// decoder.addBuffer(&message[0], bufLen);
// return decoder.validate(&message[0] + bufLen, decoder.checksumByteSize());
// }
// The return value is true.
//
// Example 2, decoding will fail if the order of messages is wrong:
//
// bool testChecksumOrder(void* buf1, size_t bufLen1,
// void* buf2, size_t bufLen2) {
// // encoding messages
// ChecksumCalculator encoder;
// encoder.setVersion(1);
//
// std::vector<unsigned char> message1(bufLen1 + encoder.checksumByteSize());
// std::vector<unsigned char> message2(bufLen2 + encoder.checksumByteSize());
//
// encoder.addBuffer(buf1, bufLen1);
// std::vector<unsigned char> message1(bufLen1 + encoder.checksumByteSize());
// memcpy(&message1[0], buf1, bufLen1);
// encoder.writeChecksum(&message1[0] + bufLen1, encoder.checksumByteSize());
//
// encoder.addBuffer(buf2, bufLen2);
// std::vector<unsigned char> message2(bufLen2 + encoder.checksumByteSize());
// memcpy(&message2[0], buf2, bufLen2);
// encoder.writeChecksum(&message2[0] + bufLen2, encoder.checksumByteSize());
//
// // decoding messages
// ChecksumCalculator decoder;
// decoder.setVersion(1);
// decoder.addBuffer(&message2[0], bufLen2);
// // returns false because the decoding order is not consistent with
// // encoding order
// if (!decoder.validate(&message2[0]+bufLen2, decoder.checksumByteSize())) {
// return false;
// }
//
// decoder.addBuffer(&message1[0], bufLen1);
// if (!decoder.validate(&message1[0]+bufLen1, decoder.checksumByteSize())) {
// return false;
// }
//
// return false;
// }
class ChecksumCalculator {
public:
// Get and set current checksum version
uint32_t getVersion() const { return m_version; }
// Call setVersion to set a checksum version. It should be called before
// addBuffer(), writeChecksum() and validate(). And it should be called
// exact once per rendering thread if both host and guest support checksum.
// It won't be called if either host or guest does not support checksum.
bool setVersion(uint32_t version);
// Maximum supported checksum version
static uint32_t getMaxVersion();
// A version string that looks like "ANDROID_EMU_CHECKSUM_HELPER_v1"
// Used multiple times when the guest queries the maximum supported version
// from the host.
// The library owns the returned pointer. The returned pointer will be
// deconstructed when unloading library.
static const char* getMaxVersionStr();
static const char* getMaxVersionStrPrefix();
// Size of checksum in the current version
size_t checksumByteSize() const;
// Update the current checksum value from the data
// at |buf| of |bufLen| bytes. Once all buffers
// have been added, call writeChecksum() to store
// the final checksum value and reset its state.
void addBuffer(const void* buf, size_t bufLen);
// Write the checksum from the list of buffers to outputChecksum
// Will reset the list of buffers by calling resetChecksum.
// Return false if the buffer is not long enough
// Please query buffer size from checksumByteSize()
bool writeChecksum(void* outputChecksum, size_t outputChecksumLen);
// Restore the states for computing checksums.
// Automatically called at the end of writeChecksum and validate.
// Can also be used to abandon the current checksum being calculated.
// Notes: it doesn't update the internal read / write counter
void resetChecksum();
// Calculate the checksum from the list of buffers and
// compare it with the checksum encoded in expectedChecksum
// Will reset the list of buffers by calling resetChecksum.
bool validate(const void* expectedChecksum, size_t expectedChecksumLen);
protected:
uint32_t m_version = 0;
// A temporary state used to compute the total length of a list of buffers,
// if addBuffer is called.
uint32_t m_numRead = 0;
uint32_t m_numWrite = 0;
// m_isEncodingChecksum is true when between addBuffer and writeChecksum
bool m_isEncodingChecksum = false;
private:
// Compute a 32bit checksum
// Used in protocol v1
uint32_t computeV1Checksum();
// The buffer used in protocol version 1 to compute checksum.
uint32_t m_v1BufferTotalLength = 0;
};

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ChecksumCalculatorThreadInfo.h"
#include "emugl/common/crash_reporter.h"
#include "emugl/common/lazy_instance.h"
#include "emugl/common/thread_store.h"
#include <stdio.h>
#include <atomic>
#include <string>
namespace {
class ChecksumCalculatorThreadStore : public ::emugl::ThreadStore {
public:
ChecksumCalculatorThreadStore() : ::emugl::ThreadStore(NULL) {}
};
#ifdef TRACE_CHECKSUMHELPER
std::atomic<size_t> sNumInstances(0);
#endif // TRACE_CHECKSUMHELPER
}
static ::emugl::LazyInstance<ChecksumCalculatorThreadStore> s_tls =
LAZY_INSTANCE_INIT;
static ChecksumCalculatorThreadInfo* getChecksumCalculatorThreadInfo() {
return static_cast<ChecksumCalculatorThreadInfo*>(s_tls->get());
}
ChecksumCalculatorThreadInfo::ChecksumCalculatorThreadInfo() {
LOG_CHECKSUMHELPER(
"%s: Checksum thread created (%u instances)\n", __FUNCTION__,
(size_t)sNumInstances);
s_tls->set(this);
}
ChecksumCalculatorThreadInfo::~ChecksumCalculatorThreadInfo() {
LOG_CHECKSUMHELPER(
"%s: GLprotocol destroyed (%u instances)\n", __FUNCTION__,
(size_t)sNumInstances);
s_tls->set(NULL);
}
uint32_t ChecksumCalculatorThreadInfo::getVersion() {
return getChecksumCalculatorThreadInfo()->m_protocol.getVersion();
}
bool ChecksumCalculatorThreadInfo::setVersion(uint32_t version) {
return getChecksumCalculatorThreadInfo()->m_protocol.setVersion(version);
}
size_t ChecksumCalculatorThreadInfo::checksumByteSize() {
return getChecksumCalculatorThreadInfo()->m_protocol.checksumByteSize();
}
bool ChecksumCalculatorThreadInfo::writeChecksum(void* buf,
size_t bufLen,
void* outputChecksum,
size_t outputChecksumLen) {
ChecksumCalculator& protocol =
getChecksumCalculatorThreadInfo()->m_protocol;
protocol.addBuffer(buf, bufLen);
return protocol.writeChecksum(outputChecksum, outputChecksumLen);
}
bool ChecksumCalculatorThreadInfo::validate(void* buf,
size_t bufLen,
void* checksum,
size_t checksumLen) {
ChecksumCalculator& protocol =
getChecksumCalculatorThreadInfo()->m_protocol;
protocol.addBuffer(buf, bufLen);
return protocol.validate(checksum, checksumLen);
}
void ChecksumCalculatorThreadInfo::validOrDie(void* buf,
size_t bufLen,
void* checksum,
size_t checksumLen,
const char* message) {
// We should actually call crashhandler_die(message), but I don't think we
// can link to that library from here
if (!validate(buf, bufLen, checksum, checksumLen)) {
emugl_crash_reporter(message);
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "ChecksumCalculator.h"
// ChecksumCalculatorThreadInfo is the class that makes ChecksumCalculator
// thread-safe. On the host, please only use ChecksumCalculator through this
// class.
class ChecksumCalculatorThreadInfo {
public:
ChecksumCalculatorThreadInfo();
~ChecksumCalculatorThreadInfo();
static uint32_t getVersion();
static bool setVersion(uint32_t version);
static uint32_t getMaxVersion();
static const char* getMaxVersionString() {
return ChecksumCalculator::getMaxVersionStr();
}
static size_t checksumByteSize();
static bool writeChecksum(void* buf,
size_t bufLen,
void* outputChecksum,
size_t outputChecksumLen);
static bool validate(void* buf,
size_t bufLen,
void* checksum,
size_t checksumLen);
static void validOrDie(void* buf,
size_t bufLen,
void* checksum,
size_t checksumLen,
const char* message);
private:
ChecksumCalculator m_protocol;
};

View file

@ -0,0 +1,28 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _ERROR_LOG_H_
#define _ERROR_LOG_H_
#include <stdio.h>
#define ERR(...) fprintf(stderr, __VA_ARGS__)
#define EMUGL_DEBUG
#ifdef EMUGL_DEBUG
# define DBG(...) fprintf(stderr, __VA_ARGS__)
#else
# define DBG(...) ((void)0)
#endif
#endif // _ERROR_LOG_H_

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <vector>
#include <string>
#include <assert.h>
#include <string.h>
// Convenient class used to hold the common context data shared
// by both the GLESv1 and GLESv2 decoders. This corresponds to
// vertex attribute buffers.
class GLDecoderContextData {
public:
// List of supported vertex attribute indices, as they appear in
// a glVertexAttribPointer() call.
typedef enum {
VERTEX_LOCATION = 0,
NORMAL_LOCATION = 1,
COLOR_LOCATION = 2,
POINTSIZE_LOCATION = 3,
TEXCOORD0_LOCATION = 4,
TEXCOORD1_LOCATION = 5,
TEXCOORD2_LOCATION = 6,
TEXCOORD3_LOCATION = 7,
TEXCOORD4_LOCATION = 8,
TEXCOORD5_LOCATION = 9,
TEXCOORD6_LOCATION = 10,
TEXCOORD7_LOCATION = 11,
MATRIXINDEX_LOCATION = 12,
WEIGHT_LOCATION = 13,
LAST_LOCATION = 14
} PointerDataLocation;
// Default constructor.
GLDecoderContextData(int numLocations = kMaxVertexAttributes)
: mPointerData(),
mNumLocations(static_cast<unsigned>(numLocations)) {
mPointerData.resize(mNumLocations);
}
// Store |len| bytes from |data| into the buffer associated with
// vertex attribute index |loc|.
void storePointerData(unsigned int loc, void *data, size_t len) {
if (loc < mNumLocations) {
std::string& ptrData = mPointerData[loc];
ptrData.assign(reinterpret_cast<char*>(data), len);
} else {
// User error, don't do anything here
}
}
// Return pointer to data associated with vertex attribute index |loc|
void* pointerData(unsigned int loc) const {
if (loc < mNumLocations) {
return const_cast<char*>(mPointerData[loc].c_str());
} else {
// User error. Return nullptr.
return nullptr;
}
}
private:
static const int kMaxVertexAttributes = 64;
std::vector<std::string> mPointerData;
unsigned mNumLocations = 0;
};

View file

@ -0,0 +1,13 @@
ROOT=../..
include $(ROOT)/make/commondefs
CXXFILES = TcpStream.cpp GLClientState.cpp glUtils.cpp
CXXINCS += -I$(ROOT)/libs/GLESv1 -I$(ROOT)/include
LIBRARY_NAME = libcodecCommon.a
include $(COMMONRULES)

View file

@ -0,0 +1,193 @@
#ifndef EMUGL_PROTOCOL_UTILS_H
#define EMUGL_PROTOCOL_UTILS_H
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
namespace emugl {
// Helper macro
#define COMPILE_ASSERT(cond) static char kAssert##__LINE__[1 - 2 * !(cond)] __attribute__((unused)) = { 0 }
// Helper template: is_pointer.
// is_pointer<T>::value is true iff |T| is a pointer type.
template <typename T> struct is_pointer {
static const bool value = false;
};
template <typename T> struct is_pointer<T*> {
static const bool value = true;
};
// A helper template to extract values form the wire protocol stream
// and convert them to appropriate host values.
//
// The wire protocol uses 32-bit exclusively when transferring
// GLintptr or GLsizei values, as well as opaque handles like GLeglImage,
// from the guest (even when the guest is 64-bit).
//
// The corresponding host definitions depend on the host bitness. For
// example, GLintptr is 64-bit on linux-x86_64. The following is a set
// of templates that can simplify the conversion of protocol values
// into host ones.
//
// The most important one is:
//
// unpack<HOST_TYPE,SIZE_TYPE>(const void* ptr)
//
// Which reads bytes from |ptr|, using |SIZE_TYPE| as the underlying
// sized-integer specifier (e.g. 'uint32_t'), and converting the result
// into a |HOST_TYPE| value. For example:
//
// unpack<EGLImage,uint32_t>(ptr + 12);
//
// will read a 4-byte value from |ptr + 12| and convert it into
// an EGLImage, which is a host void*. The template detects host
// pointer types to perform proper type casting.
//
// TODO(digit): Add custom unpackers to handle generic opaque void* values.
// and map them to unique 32-bit values.
template <typename T, typename S, bool IS_POINTER>
struct UnpackerT {};
template <typename T, typename S>
struct UnpackerT<T,S,false> {
static inline T unpack(const void* ptr) {
COMPILE_ASSERT(sizeof(T) == sizeof(S));
return (T)(*(S*)(ptr));
}
};
template <typename T, typename S>
struct UnpackerT<T,S,true> {
static inline T unpack(const void* ptr) {
return (T)(uintptr_t)(*(S*)(ptr));
}
};
template <>
struct UnpackerT<float,uint32_t,false> {
static inline float unpack(const void* ptr) {
union {
float f;
uint32_t u;
} v;
v.u = *(uint32_t*)(ptr);
return v.f;
}
};
template <>
struct UnpackerT<double,uint64_t,false> {
static inline double unpack(const void* ptr) {
union {
double d;
uint32_t u;
} v;
v.u = *(uint64_t*)(ptr);
return v.d;
}
};
template <>
struct UnpackerT<ssize_t,uint32_t,false> {
static inline ssize_t unpack(const void* ptr) {
return (ssize_t)*(int32_t*)(ptr);
}
};
template <typename T, typename S>
inline T Unpack(const void* ptr) {
return UnpackerT<T, S, is_pointer<T>::value>::unpack(ptr);
}
// Helper class used to ensure input buffers passed to EGL/GL functions
// are properly aligned (preventing crashes with some backends).
// Usage example:
//
// InputBuffer inputBuffer(ptr, size);
// glDoStuff(inputBuffer.get());
//
// inputBuffer.get() will return the original value of |ptr| if it was
// aligned on an 8-byte boundary. Otherwise, it will return the address
// of an aligned heap-allocated copy of the original |size| bytes starting
// from |ptr|. The heap block is released at scope exit.
class InputBuffer {
public:
InputBuffer(const void* input, size_t size, size_t align = 8) :
mBuff(input), mIsCopy(false) {
if (((uintptr_t)input & (align - 1U)) != 0) {
void* newBuff = malloc(size);
memcpy(newBuff, input, size);
mBuff = newBuff;
mIsCopy = true;
}
}
~InputBuffer() {
if (mIsCopy) {
free((void*)mBuff);
}
}
const void* get() const {
return mBuff;
}
private:
const void* mBuff;
bool mIsCopy;
};
// Helper class used to ensure that output buffers passed to EGL/GL functions
// are aligned on 8-byte addresses.
// Usage example:
//
// ptr = stream->alloc(size);
// OutputBuffer outputBuffer(ptr, size);
// glGetStuff(outputBuffer.get());
// outputBuffer.flush();
//
// outputBuffer.get() returns the original value of |ptr| if it was already
// aligned on an 8=byte boundary. Otherwise, it returns the size of an heap
// allocated zeroed buffer of |size| bytes.
//
// outputBuffer.flush() copies the content of the heap allocated buffer back
// to |ptr| explictly, if needed. If a no-op if |ptr| was aligned.
class OutputBuffer {
public:
OutputBuffer(unsigned char* ptr, size_t size, size_t align = 8) :
mOrgBuff(ptr), mBuff(ptr), mSize(size) {
if (((uintptr_t)ptr & (align - 1U)) != 0) {
void* newBuff = calloc(1, size);
mBuff = newBuff;
}
}
~OutputBuffer() {
if (mBuff != mOrgBuff) {
free(mBuff);
}
}
void* get() const {
return mBuff;
}
void flush() {
if (mBuff != mOrgBuff) {
memcpy(mOrgBuff, mBuff, mSize);
}
}
private:
unsigned char* mOrgBuff;
void* mBuff;
size_t mSize;
};
} // namespace emugl
#endif // EMUGL_PROTOCOL_UTILS_H

View file

@ -0,0 +1,416 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "glUtils.h"
#include "ErrorLog.h"
#include <string.h>
size_t glSizeof(GLenum type)
{
size_t retval = 0;
switch(type) {
case GL_BYTE:
case GL_UNSIGNED_BYTE:
retval = 1;
break;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
case GL_HALF_FLOAT_OES:
retval = 2;
break;
case GL_INT:
case GL_FLOAT:
case GL_FIXED:
case GL_BOOL:
retval = 4;
break;
#ifdef GL_DOUBLE
case GL_DOUBLE:
retval = 8;
break;
#endif
case GL_FLOAT_VEC2:
case GL_INT_VEC2:
case GL_BOOL_VEC2:
retval = 8;
break;
case GL_INT_VEC3:
case GL_BOOL_VEC3:
case GL_FLOAT_VEC3:
retval = 12;
break;
case GL_FLOAT_VEC4:
case GL_BOOL_VEC4:
case GL_INT_VEC4:
case GL_FLOAT_MAT2:
retval = 16;
break;
case GL_FLOAT_MAT3:
retval = 36;
break;
case GL_FLOAT_MAT4:
retval = 64;
break;
case GL_SAMPLER_2D:
case GL_SAMPLER_CUBE:
retval = 4;
break;
default:
ERR("**** ERROR unknown type 0x%x (%s,%d)\n", type, __FUNCTION__,__LINE__);
}
return retval;
}
size_t glUtilsParamSize(GLenum param)
{
size_t s = 0;
switch(param)
{
case GL_DEPTH_TEST:
case GL_DEPTH_FUNC:
case GL_DEPTH_BITS:
case GL_MAX_CLIP_PLANES:
case GL_GREEN_BITS:
case GL_MAX_MODELVIEW_STACK_DEPTH:
case GL_MAX_PROJECTION_STACK_DEPTH:
case GL_MAX_TEXTURE_STACK_DEPTH:
case GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES:
case GL_IMPLEMENTATION_COLOR_READ_TYPE_OES:
case GL_NUM_COMPRESSED_TEXTURE_FORMATS:
case GL_MAX_TEXTURE_SIZE:
case GL_TEXTURE_GEN_MODE_OES:
case GL_TEXTURE_ENV_MODE:
case GL_FOG_MODE:
case GL_FOG_DENSITY:
case GL_FOG_START:
case GL_FOG_END:
case GL_SPOT_EXPONENT:
case GL_CONSTANT_ATTENUATION:
case GL_LINEAR_ATTENUATION:
case GL_QUADRATIC_ATTENUATION:
case GL_SHININESS:
case GL_LIGHT_MODEL_TWO_SIDE:
case GL_POINT_SIZE:
case GL_POINT_SIZE_MIN:
case GL_POINT_SIZE_MAX:
case GL_POINT_FADE_THRESHOLD_SIZE:
case GL_CULL_FACE_MODE:
case GL_FRONT_FACE:
case GL_SHADE_MODEL:
case GL_DEPTH_WRITEMASK:
case GL_DEPTH_CLEAR_VALUE:
case GL_STENCIL_FAIL:
case GL_STENCIL_PASS_DEPTH_FAIL:
case GL_STENCIL_PASS_DEPTH_PASS:
case GL_STENCIL_REF:
case GL_STENCIL_WRITEMASK:
case GL_MATRIX_MODE:
case GL_MODELVIEW_STACK_DEPTH:
case GL_PROJECTION_STACK_DEPTH:
case GL_TEXTURE_STACK_DEPTH:
case GL_ALPHA_TEST_FUNC:
case GL_ALPHA_TEST_REF:
case GL_ALPHA_TEST:
case GL_BLEND_DST:
case GL_BLEND_SRC:
case GL_BLEND:
case GL_LOGIC_OP_MODE:
case GL_SCISSOR_TEST:
case GL_MAX_TEXTURE_UNITS:
case GL_ACTIVE_TEXTURE:
case GL_ALPHA_BITS:
case GL_ARRAY_BUFFER_BINDING:
case GL_BLUE_BITS:
case GL_CLIENT_ACTIVE_TEXTURE:
case GL_CLIP_PLANE0:
case GL_CLIP_PLANE1:
case GL_CLIP_PLANE2:
case GL_CLIP_PLANE3:
case GL_CLIP_PLANE4:
case GL_CLIP_PLANE5:
case GL_COLOR_ARRAY:
case GL_COLOR_ARRAY_BUFFER_BINDING:
case GL_COLOR_ARRAY_SIZE:
case GL_COLOR_ARRAY_STRIDE:
case GL_COLOR_ARRAY_TYPE:
case GL_COLOR_LOGIC_OP:
case GL_COLOR_MATERIAL:
case GL_PACK_ALIGNMENT:
case GL_PERSPECTIVE_CORRECTION_HINT:
case GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES:
case GL_POINT_SIZE_ARRAY_STRIDE_OES:
case GL_POINT_SIZE_ARRAY_TYPE_OES:
case GL_POINT_SMOOTH:
case GL_POINT_SMOOTH_HINT:
case GL_POINT_SPRITE_OES:
case GL_COORD_REPLACE_OES:
case GL_COMBINE_ALPHA:
case GL_SRC0_RGB:
case GL_SRC1_RGB:
case GL_SRC2_RGB:
case GL_OPERAND0_RGB:
case GL_OPERAND1_RGB:
case GL_OPERAND2_RGB:
case GL_SRC0_ALPHA:
case GL_SRC1_ALPHA:
case GL_SRC2_ALPHA:
case GL_OPERAND0_ALPHA:
case GL_OPERAND1_ALPHA:
case GL_OPERAND2_ALPHA:
case GL_RGB_SCALE:
case GL_ALPHA_SCALE:
case GL_COMBINE_RGB:
case GL_POLYGON_OFFSET_FACTOR:
case GL_POLYGON_OFFSET_FILL:
case GL_POLYGON_OFFSET_UNITS:
case GL_RED_BITS:
case GL_RESCALE_NORMAL:
case GL_SAMPLE_ALPHA_TO_COVERAGE:
case GL_SAMPLE_ALPHA_TO_ONE:
case GL_SAMPLE_BUFFERS:
case GL_SAMPLE_COVERAGE:
case GL_SAMPLE_COVERAGE_INVERT:
case GL_SAMPLE_COVERAGE_VALUE:
case GL_SAMPLES:
case GL_STENCIL_BITS:
case GL_STENCIL_CLEAR_VALUE:
case GL_STENCIL_FUNC:
case GL_STENCIL_TEST:
case GL_STENCIL_VALUE_MASK:
case GL_STENCIL_BACK_FUNC:
case GL_STENCIL_BACK_VALUE_MASK:
case GL_STENCIL_BACK_REF:
case GL_STENCIL_BACK_FAIL:
case GL_STENCIL_BACK_PASS_DEPTH_FAIL:
case GL_STENCIL_BACK_PASS_DEPTH_PASS:
case GL_STENCIL_BACK_WRITEMASK:
case GL_TEXTURE_2D:
case GL_TEXTURE_BINDING_2D:
case GL_TEXTURE_BINDING_CUBE_MAP:
case GL_TEXTURE_BINDING_EXTERNAL_OES:
case GL_TEXTURE_COORD_ARRAY:
case GL_TEXTURE_COORD_ARRAY_BUFFER_BINDING:
case GL_TEXTURE_COORD_ARRAY_SIZE:
case GL_TEXTURE_COORD_ARRAY_STRIDE:
case GL_TEXTURE_COORD_ARRAY_TYPE:
case GL_UNPACK_ALIGNMENT:
case GL_VERTEX_ARRAY:
case GL_VERTEX_ARRAY_BUFFER_BINDING:
case GL_VERTEX_ARRAY_SIZE:
case GL_VERTEX_ARRAY_STRIDE:
case GL_VERTEX_ARRAY_TYPE:
case GL_SPOT_CUTOFF:
case GL_TEXTURE_MIN_FILTER:
case GL_TEXTURE_MAG_FILTER:
case GL_TEXTURE_WRAP_S:
case GL_TEXTURE_WRAP_T:
case GL_GENERATE_MIPMAP:
case GL_GENERATE_MIPMAP_HINT:
case GL_RENDERBUFFER_WIDTH_OES:
case GL_RENDERBUFFER_HEIGHT_OES:
case GL_RENDERBUFFER_INTERNAL_FORMAT_OES:
case GL_RENDERBUFFER_RED_SIZE_OES:
case GL_RENDERBUFFER_GREEN_SIZE_OES:
case GL_RENDERBUFFER_BLUE_SIZE_OES:
case GL_RENDERBUFFER_ALPHA_SIZE_OES:
case GL_RENDERBUFFER_DEPTH_SIZE_OES:
case GL_RENDERBUFFER_STENCIL_SIZE_OES:
case GL_RENDERBUFFER_BINDING:
case GL_FRAMEBUFFER_BINDING:
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_OES:
case GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_OES:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_OES:
case GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_OES:
case GL_FENCE_STATUS_NV:
case GL_FENCE_CONDITION_NV:
case GL_TEXTURE_WIDTH_QCOM:
case GL_TEXTURE_HEIGHT_QCOM:
case GL_TEXTURE_DEPTH_QCOM:
case GL_TEXTURE_INTERNAL_FORMAT_QCOM:
case GL_TEXTURE_FORMAT_QCOM:
case GL_TEXTURE_TYPE_QCOM:
case GL_TEXTURE_IMAGE_VALID_QCOM:
case GL_TEXTURE_NUM_LEVELS_QCOM:
case GL_TEXTURE_TARGET_QCOM:
case GL_TEXTURE_OBJECT_VALID_QCOM:
case GL_BLEND_EQUATION_RGB_OES:
case GL_BLEND_EQUATION_ALPHA_OES:
case GL_BLEND_DST_RGB_OES:
case GL_BLEND_SRC_RGB_OES:
case GL_BLEND_DST_ALPHA_OES:
case GL_BLEND_SRC_ALPHA_OES:
case GL_MAX_LIGHTS:
case GL_SHADER_TYPE:
case GL_DELETE_STATUS:
case GL_COMPILE_STATUS:
case GL_INFO_LOG_LENGTH:
case GL_SHADER_SOURCE_LENGTH:
case GL_CURRENT_PROGRAM:
case GL_LINK_STATUS:
case GL_VALIDATE_STATUS:
case GL_ATTACHED_SHADERS:
case GL_ACTIVE_UNIFORMS:
case GL_ACTIVE_ATTRIBUTES:
case GL_SUBPIXEL_BITS:
case GL_MAX_CUBE_MAP_TEXTURE_SIZE:
case GL_NUM_SHADER_BINARY_FORMATS:
case GL_SHADER_COMPILER:
case GL_MAX_VERTEX_ATTRIBS:
case GL_MAX_VERTEX_UNIFORM_VECTORS:
case GL_MAX_VARYING_VECTORS:
case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
case GL_MAX_FRAGMENT_UNIFORM_VECTORS:
case GL_MAX_RENDERBUFFER_SIZE:
case GL_MAX_TEXTURE_IMAGE_UNITS:
case GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES:
case GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES:
case GL_LINE_WIDTH:
s = 1;
break;
case GL_ALIASED_LINE_WIDTH_RANGE:
case GL_ALIASED_POINT_SIZE_RANGE:
case GL_DEPTH_RANGE:
case GL_MAX_VIEWPORT_DIMS:
case GL_SMOOTH_POINT_SIZE_RANGE:
case GL_SMOOTH_LINE_WIDTH_RANGE:
s= 2;
break;
case GL_SPOT_DIRECTION:
case GL_POINT_DISTANCE_ATTENUATION:
case GL_CURRENT_NORMAL:
s = 3;
break;
case GL_CURRENT_VERTEX_ATTRIB:
case GL_CURRENT_TEXTURE_COORDS:
case GL_CURRENT_COLOR:
case GL_FOG_COLOR:
case GL_AMBIENT:
case GL_DIFFUSE:
case GL_SPECULAR:
case GL_EMISSION:
case GL_POSITION:
case GL_LIGHT_MODEL_AMBIENT:
case GL_TEXTURE_ENV_COLOR:
case GL_SCISSOR_BOX:
case GL_VIEWPORT:
case GL_TEXTURE_CROP_RECT_OES:
case GL_COLOR_CLEAR_VALUE:
case GL_COLOR_WRITEMASK:
case GL_AMBIENT_AND_DIFFUSE:
case GL_BLEND_COLOR:
s = 4;
break;
case GL_MODELVIEW_MATRIX:
case GL_PROJECTION_MATRIX:
case GL_TEXTURE_MATRIX:
s = 16;
break;
default:
ERR("glUtilsParamSize: unknow param 0x%08x\n", param);
s = 1; // assume 1
}
return s;
}
void glUtilsPackPointerData(unsigned char *dst, unsigned char *src,
int size, GLenum type, unsigned int stride,
unsigned int datalen)
{
unsigned int vsize = size * glSizeof(type);
if (stride == 0) stride = vsize;
if (stride == vsize) {
memcpy(dst, src, datalen);
} else {
for (unsigned int i = 0; i < datalen; i += vsize) {
memcpy(dst, src, vsize);
dst += vsize;
src += stride;
}
}
}
int glUtilsPixelBitSize(GLenum format, GLenum type)
{
int components = 0;
int componentsize = 0;
int pixelsize = 0;
switch(type) {
case GL_BYTE:
case GL_UNSIGNED_BYTE:
componentsize = 8;
break;
case GL_SHORT:
case GL_UNSIGNED_SHORT:
case GL_UNSIGNED_SHORT_5_6_5:
case GL_UNSIGNED_SHORT_4_4_4_4:
case GL_UNSIGNED_SHORT_5_5_5_1:
case GL_RGB565_OES:
case GL_RGB5_A1_OES:
case GL_RGBA4_OES:
pixelsize = 16;
break;
case GL_INT:
case GL_UNSIGNED_INT:
case GL_FLOAT:
case GL_FIXED:
case GL_UNSIGNED_INT_24_8_OES:
pixelsize = 32;
break;
default:
ERR("glUtilsPixelBitSize: unknown pixel type - assuming pixel data 0\n");
componentsize = 0;
}
if (pixelsize == 0) {
switch(format) {
#if 0
case GL_RED:
case GL_GREEN:
case GL_BLUE:
#endif
case GL_ALPHA:
case GL_LUMINANCE:
case GL_DEPTH_COMPONENT:
case GL_DEPTH_STENCIL_OES:
components = 1;
break;
case GL_LUMINANCE_ALPHA:
components = 2;
break;
case GL_RGB:
#if 0
case GL_BGR:
#endif
components = 3;
break;
case GL_RGBA:
case GL_BGRA_EXT:
components = 4;
break;
default:
ERR("glUtilsPixelBitSize: unknown pixel format...\n");
components = 0;
}
pixelsize = components * componentsize;
}
return pixelsize;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
size_t glSizeof(GLenum type);
size_t glUtilsParamSize(GLenum param);
void glUtilsPackPointerData(unsigned char *dst, unsigned char *str,
int size, GLenum type, unsigned int stride,
unsigned int datalen);
int glUtilsPixelBitSize(GLenum format, GLenum type);
#ifdef __cplusplus
};
#endif

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __GL_BASE_TYPES__H
#define __GL_BASE_TYPES__H
#include <KHR/khrplatform.h>
#ifndef gles1_APIENTRY
#define gles1_APIENTRY KHRONOS_APIENTRY
#endif
#ifndef gles2_APIENTRY
#define gles2_APIENTRY KHRONOS_APIENTRY
#endif
typedef void GLvoid;
typedef unsigned int GLenum;
typedef unsigned char GLboolean;
typedef unsigned int GLbitfield;
typedef char GLchar;
typedef khronos_int8_t GLbyte;
typedef short GLshort;
typedef int GLint;
typedef int GLsizei;
typedef khronos_uint8_t GLubyte;
typedef unsigned short GLushort;
typedef unsigned int GLuint;
typedef khronos_float_t GLfloat;
typedef khronos_float_t GLclampf;
typedef khronos_int32_t GLfixed;
typedef khronos_int32_t GLclampx;
typedef khronos_intptr_t GLintptr;
typedef khronos_ssize_t GLsizeiptr;
typedef char *GLstr;
/* JR XXX Treating this as an in handle - is this correct? */
typedef void * GLeglImageOES;
/* ErrorCode */
#ifndef GL_INVALID_ENUM
#define GL_NO_ERROR 0
#define GL_INVALID_ENUM 0x0500
#define GL_INVALID_VALUE 0x0501
#define GL_INVALID_OPERATION 0x0502
#define GL_STACK_OVERFLOW 0x0503
#define GL_STACK_UNDERFLOW 0x0504
#define GL_OUT_OF_MEMORY 0x0505
#endif
#endif

View file

@ -0,0 +1 @@
add_subdirectory(common)

View file

@ -0,0 +1,65 @@
# This build script corresponds to a library containing many definitions
# common to both the guest and the host. They relate to
#
LOCAL_PATH := $(call my-dir)
### emugl_common host library ###########################################
commonSources := \
crash_reporter.cpp \
id_to_object_map.cpp \
lazy_instance.cpp \
logging.cpp \
message_channel.cpp \
pod_vector.cpp \
shared_library.cpp \
smart_ptr.cpp \
sockets.cpp \
thread_store.cpp \
host_commonSources := $(commonSources)
host_commonLdLibs := $(CXX_STD_LIB)
ifneq (windows,$(BUILD_TARGET_OS))
host_commonSources += \
thread_pthread.cpp \
host_commonLdLibs += -ldl -lpthread
else
host_commonSources += \
condition_variable_win32.cpp \
thread_win32.cpp \
endif
$(call emugl-begin-host-static-library,libemugl_common)
LOCAL_SRC_FILES := $(host_commonSources)
$(call emugl-export,C_INCLUDES,$(EMUGL_PATH)/shared)
$(call emugl-export,LDLIBS,$(host_commonLdLibs))
$(call emugl-end-module)
### emugl_common_unittests ##############################################
host_commonSources := \
condition_variable_unittest.cpp \
id_to_object_map_unittest.cpp \
lazy_instance_unittest.cpp \
pod_vector_unittest.cpp \
message_channel_unittest.cpp \
mutex_unittest.cpp \
shared_library_unittest.cpp \
smart_ptr_unittest.cpp \
thread_store_unittest.cpp \
thread_unittest.cpp \
unique_integer_map_unittest.cpp \
$(call emugl-begin-host-executable,emugl$(BUILD_TARGET_SUFFIX)_common_host_unittests)
LOCAL_SRC_FILES := $(host_commonSources)
$(call emugl-import,libemugl_common libemugl_gtest)
$(call emugl-end-module)
$(call emugl-begin-host-shared-library,lib$(BUILD_TARGET_SUFFIX)emugl_test_shared_library)
LOCAL_SRC_FILES := testing/test_shared_library.cpp
LOCAL_CFLAGS := -fvisibility=default
$(call emugl-end-module)

View file

@ -0,0 +1,16 @@
set(COMMON_SOURCES
crash_reporter.cpp
id_to_object_map.cpp
lazy_instance.cpp
logging.cpp
message_channel.cpp
pod_vector.cpp
shared_library.cpp
smart_ptr.cpp
sockets.cpp
thread_store.cpp
thread_pthread.cpp)
add_library(emugl_common ${COMMON_SOURCES})
target_link_libraries(emugl_common
dl pthread)

View file

@ -0,0 +1,91 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_CONDITION_VARIABLE_H
#define EMUGL_CONDITION_VARIABLE_H
#include "emugl/common/mutex.h"
#include "emugl/common/pod_vector.h"
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
namespace emugl {
// A class that implements a condition variable, which can be used in
// association with a Mutex to blocking-wait for specific conditions.
// Useful to implement various synchronization data structures.
// For an example, see ::emugl::MessageChannel.
class ConditionVariable {
public:
#ifdef _WIN32
// Default constructor.
ConditionVariable();
// Destructor.
~ConditionVariable();
// Wait until the condition variable is signaled. Note that spurious
// wakeups are always a possibility, so always check the condition
// in a loop, i.e. do:
//
// while (!condition) { condVar.wait(&lock); }
//
// instead of:
//
// if (!condition) { condVar.wait(&lock); }
//
void wait(Mutex* userLock);
// Signal that a condition was reached. This will wake at most one
// waiting thread that is blocked on wait().
void signal();
private:
PodVector<HANDLE> mWaiters;
Mutex mLock;
#else // !_WIN32
// Note: on Posix systems, make it a naive wrapper around pthread_cond_t.
ConditionVariable() {
pthread_cond_init(&mCond, NULL);
}
~ConditionVariable() {
pthread_cond_destroy(&mCond);
}
void wait(Mutex* userLock) {
pthread_cond_wait(&mCond, &userLock->mLock);
}
void signal() {
pthread_cond_signal(&mCond);
}
private:
pthread_cond_t mCond;
#endif // !_WIN32
};
} // namespace emugl
#endif // EMUGL_CONDITION_VARIABLE_H

View file

@ -0,0 +1,27 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/condition_variable.h"
#include "emugl/common/mutex.h"
#include <gtest/gtest.h>
namespace emugl {
TEST(ConditionVariable, init) {
ConditionVariable cond;
}
} // namespace emugl

View file

@ -0,0 +1,114 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/condition_variable.h"
#include "emugl/common/lazy_instance.h"
#include "emugl/common/mutex.h"
#include "emugl/common/pod_vector.h"
// Technical note: this is loosely based on the Chromium implementation
// of ConditionVariable. This version works on Windows XP and above and
// doesn't try to use Vista's CONDITION_VARIABLE types.
namespace emugl {
namespace {
// Helper class which implements a free list of event handles.
class WaitEventStorage {
public:
WaitEventStorage() : mFreeHandles(), mLock() {}
~WaitEventStorage() {
for (size_t n = 0; n < mFreeHandles.size(); ++n) {
CloseHandle(mFreeHandles[n]);
}
}
HANDLE alloc() {
HANDLE handle;
mLock.lock();
size_t size = mFreeHandles.size();
if (size > 0) {
handle = mFreeHandles[size - 1U];
mFreeHandles.remove(size - 1U);
} else {
handle = CreateEvent(NULL, TRUE, FALSE, NULL);
}
mLock.unlock();
return handle;
}
void free(HANDLE h) {
mLock.lock();
ResetEvent(h);
mFreeHandles.push_back(h);
mLock.unlock();
}
private:
PodVector<HANDLE> mFreeHandles;
Mutex mLock;
};
LazyInstance<WaitEventStorage> sWaitEvents = LAZY_INSTANCE_INIT;
} // namespace
ConditionVariable::ConditionVariable() : mWaiters(), mLock() {}
ConditionVariable::~ConditionVariable() {
mLock.lock();
for (size_t n = 0; n < mWaiters.size(); ++n) {
CloseHandle(mWaiters[n]);
}
mWaiters.resize(0U);
mLock.unlock();
}
void ConditionVariable::wait(Mutex* userLock) {
// Grab new waiter event handle.
mLock.lock();
HANDLE handle = sWaitEvents->alloc();
mWaiters.push_back(handle);
mLock.unlock();
// Unlock user lock then wait for event.
userLock->unlock();
WaitForSingleObject(handle, INFINITE);
// NOTE: The handle has been removed from mWaiters here,
// see signal() below. Close/recycle the event.
sWaitEvents->free(handle);
userLock->lock();
}
void ConditionVariable::signal() {
mLock.lock();
size_t size = mWaiters.size();
if (size > 0U) {
// NOTE: This wakes up the thread that went to sleep most
// recently (LIFO) for performance reason. For better
// fairness, using (FIFO) would be appropriate.
HANDLE handle = mWaiters[size - 1U];
mWaiters.remove(size - 1U);
SetEvent(handle);
// NOTE: The handle will be closed/recycled by the waiter.
} else {
// Nothing to signal.
}
mLock.unlock();
}
} // namespace emugl

View file

@ -0,0 +1,33 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "crash_reporter.h"
#include <cstdlib>
void default_crash_reporter(const char* format, ...) {
abort();
}
crash_reporter_t emugl_crash_reporter = default_crash_reporter;
void set_emugl_crash_reporter(crash_reporter_t crash_reporter) {
if (crash_reporter) {
emugl_crash_reporter = crash_reporter;
} else {
emugl_crash_reporter = default_crash_reporter;
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
typedef void (*crash_reporter_t)(const char* format, ...);
extern crash_reporter_t emugl_crash_reporter;
void set_emugl_crash_reporter(crash_reporter_t crash_reporter);

View file

@ -0,0 +1,236 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/id_to_object_map.h"
#include <stdlib.h>
namespace emugl {
namespace {
typedef IdToObjectMapBase::KeyType KeyType;
enum {
kMinShift = 3,
kMaxShift = 31,
kMinCapacity = (1 << kMinShift),
kLoadScale = 1024,
kMinLoad = kLoadScale/4, // 25% minimum load.
kMaxLoad = kLoadScale*3/4, // 75% maximum load.
kInvalidKey = IdToObjectMapBase::kMaxId + 1U,
kTombstone = IdToObjectMapBase::kMaxId + 2U,
};
// Return a number that indicates if the current |capacity| is appropriate
// to hold |size| items in our map.
// -1 -> the capacity is too small and needs to be increased.
// 0 -> the capacity is ok.
// +1 -> the capacity is too large and needs to be decreased.
int capacityCompare(size_t shift, size_t size) {
size_t capacity = 1U << shift;
// Essentially, one can rewrite:
// load < minLoad
// as:
// size / capacity < minLoad
// capacity * minLoad > size
if (capacity * kMinLoad > size * kLoadScale)
return +1;
// Similarly, one can rewrite:
// load > maxLoad
// as:
// size / capacity > maxLoad
// capacity * maxLoad < size
if (capacity * kMaxLoad < size * kLoadScale)
return -1;
return 0;
}
size_t probeKeys(const KeyType* keys, size_t shift, KeyType key) {
static const int kPrimes[] = {
1, /* For 1 << 0 */
2,
3,
7,
13,
31,
61,
127,
251,
509,
1021,
2039,
4093,
8191,
16381,
32749,
65521, /* For 1 << 16 */
131071,
262139,
524287,
1048573,
2097143,
4194301,
8388593,
16777213,
33554393,
67108859,
134217689,
268435399,
536870909,
1073741789,
2147483647 /* For 1 << 31 */
};
size_t slot = key % kPrimes[shift];
size_t step = 0;
for (;;) {
KeyType k = keys[slot];
if (k == kInvalidKey || k == kTombstone || k == key)
return slot;
step += 1;
slot = (slot + step) & (1U << shift);
}
}
} // namespace
IdToObjectMapBase::IdToObjectMapBase() :
mCount(0), mShift(kMinShift) {
size_t capacity = 1U << mShift;
mKeys = static_cast<KeyType*>(::calloc(sizeof(mKeys[0]), capacity));
mValues = static_cast<void**>(::calloc(sizeof(mValues[0]), capacity));
for (size_t n = 0; n < capacity; ++n) {
mKeys[n] = kInvalidKey;
}
}
IdToObjectMapBase::~IdToObjectMapBase() {
mShift = 0;
mCount = 0;
::free(mKeys);
::free(mValues);
}
bool IdToObjectMapBase::contains(KeyType key) const {
size_t slot = probeKeys(mKeys, mShift, key);
switch (mKeys[slot]) {
case kInvalidKey:
case kTombstone:
return false;
default:
;
}
return true;
}
bool IdToObjectMapBase::find(KeyType key, void** value) const {
size_t slot = probeKeys(mKeys, mShift, key);
if (!isValidKey(mKeys[slot])) {
*value = NULL;
return false;
}
*value = mValues[slot];
return true;
}
void* IdToObjectMapBase::set(KeyType key, void* value) {
if (!value)
return remove(key);
size_t slot = probeKeys(mKeys, mShift, key);
void* result;
if (isValidKey(mKeys[slot])) {
result = mValues[slot];
mValues[slot] = value;
} else {
mKeys[slot] = key;
mValues[slot] = value;
result = NULL;
mCount++;
resize(mCount);
}
return result;
}
void* IdToObjectMapBase::remove(KeyType key) {
size_t slot = probeKeys(mKeys, mShift, key);
if (!isValidKey(mKeys[slot]))
return NULL;
void* result = mValues[slot];
mValues[slot] = NULL;
mKeys[slot] = kTombstone;
mCount--;
return result;
}
void IdToObjectMapBase::resize(size_t newSize) {
int ret = capacityCompare(mShift, newSize);
if (!ret)
return;
size_t oldCapacity = 1U << mShift;
size_t newShift = mShift;
if (ret < 0) {
// Capacity is too small and must be increased.
do {
if (newShift == kMaxShift)
break;
++newShift;
} while (capacityCompare(newShift, newSize) < 0);
} else {
// Capacity is too large and must be decreased.
do {
if (newShift == kMinShift)
break;
newShift--;
} while (capacityCompare(newShift, newSize) > 0);
}
if (newShift == mShift)
return;
// Allocate new arrays.
size_t newCapacity = 1U << newShift;
KeyType* newKeys = static_cast<KeyType*>(
::calloc(sizeof(newKeys[0]), newCapacity));
void** newValues = static_cast<void**>(
::calloc(sizeof(newValues[0]), newCapacity));
for (size_t n = 0; n < newCapacity; ++n)
newKeys[n] = kInvalidKey;
// Copy old entries into new arrays.
for (size_t n = 0; n < oldCapacity; ++n) {
KeyType key = mKeys[n];
if (isValidKey(key)) {
size_t newSlot = probeKeys(newKeys, newShift, key);
newKeys[newSlot] = key;
newValues[newSlot] = mValues[n];
}
}
// Swap arrays, and get rid of old ones.
::free(mKeys);
::free(mValues);
mKeys = newKeys;
mValues = newValues;
mShift = newShift;
}
} // namespace emugl

View file

@ -0,0 +1,176 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_ID_TO_OBJECT_MAP_H
#define EMUGL_COMMON_ID_TO_OBJECT_MAP_H
#include <stddef.h>
namespace emugl {
// Base implementation class for IdToObjectMap template.
// Used to reduce template-instanciated code generation.
class IdToObjectMapBase {
public:
// The type of keys in this map.
typedef unsigned KeyType;
// Values higher than kMaxId cannot be used as map keys.
enum {
kMaxId = 0xfffffffdU,
};
static inline bool isValidKey(KeyType key) {
return key <= kMaxId;
}
protected:
IdToObjectMapBase();
~IdToObjectMapBase();
void clear();
// Return size
inline size_t size() const { return mCount; }
inline size_t capacity() const { return 1U << mShift; }
// Return true iff the map contains a given key.
bool contains(KeyType key) const;
// Find a value associated with a given |key| in the map.
// On success, return true and sets |*value| to the value/pointer,
// which is _still_ owned by the map.
// On failure, return false and sets |*value| to NULL.
bool find(KeyType key, void** value) const;
// Associate a value with a given |key| in the map.
// Return the old value for the key, if any. Caller is responsible
// for freeing it.
void* set(KeyType key, void* value);
// Remove the value associated with a given |key|.
// Return the old value, if any. Caller is responsible for
// freeing it.
void* remove(KeyType key);
size_t mCount;
size_t mShift;
KeyType* mKeys;
void** mValues;
private:
// Resize the map if needed to ensure it can hold at least |newSize|
// entries.
void resize(size_t newSize);
};
// A templated data container that acts as a dictionary mapping unsigned
// integer keys to heap-allocated objects of type T. The dictionary
// owns the objects associated with its keys, and automatically destroys
// them when it is destroyed, or during replacement or removal.
template <class T>
class IdToObjectMap : public IdToObjectMapBase {
public:
// Initialize an empty instance.
IdToObjectMap() : IdToObjectMapBase() {}
// Destroy this instance.
~IdToObjectMap() {
clear();
}
// Return the number of items in this map.
inline size_t size() const { return IdToObjectMapBase::size(); }
// Return true iff the map is empty.
inline bool empty() const { return !IdToObjectMapBase::size(); }
// Remove all items from the map.
void clear();
// Returns true iff the dictionary contains a value for |key|.
inline bool contains(KeyType key) const {
return IdToObjectMapBase::contains(key);
}
// Find the value corresponding to |key| in this map.
// On success, return true, and sets |*value| to point to the
// value (still owned by the instance). On failure, return false.
inline bool find(KeyType key, T** value) const {
return IdToObjectMapBase::find(key, reinterpret_cast<void**>(value));
}
// Return the value associated with a given |key|, or NULL if it is
// not in the map. Result is still owned by the map.
inline T* get(KeyType key) const {
T* result = NULL;
this->find(key, &result);
return result;
}
// Associate |value| with a given |key|. Returns true if a previous
// value was replaced, and false if this is the first time a value
// was associated with the given key. IMPORTANT: This transfers
// ownership of |value| to the map instance. In case of replacement,
// the old value is automatically destroyed. Using NULL as the value
// is equivalent to calling remove().
bool set(KeyType key, T* value);
// Remove any value associated with |key|.
// Return true iff a value was associated with the key and destroyed
// by this function, false if there was no value associated with the
// key (or if it was NULL).
bool remove(KeyType key);
};
template <class T>
void IdToObjectMap<T>::clear() {
size_t n = capacity();
while (n > 0) {
--n;
if (!isValidKey(mKeys[n]))
continue;
delete static_cast<T*>(mValues[n]);
mValues[n] = NULL;
mKeys[n] = kMaxId + 1U;
}
mCount = 0;
}
template <class T>
bool IdToObjectMap<T>::set(KeyType key, T* value) {
T* oldValue = static_cast<T*>(IdToObjectMapBase::set(key, value));
if (!oldValue) {
return false;
}
delete oldValue;
return true;
}
template <class T>
bool IdToObjectMap<T>::remove(KeyType key) {
T* oldValue = static_cast<T*>(IdToObjectMapBase::remove(key));
if (!oldValue)
return false;
delete oldValue;
return true;
}
} // namespace emugl
#endif // EMUGL_COMMON_ID_TO_OBJECT_MAP_H

View file

@ -0,0 +1,116 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/id_to_object_map.h"
#include <gtest/gtest.h>
namespace emugl {
namespace {
typedef IdToObjectMapBase::KeyType KeyType;
class Foo {
public:
Foo() : mVal(0) {}
Foo(int val) : mVal(val) {}
~Foo() {}
int val() const { return mVal; }
void setVal(int val) { mVal = val; }
private:
int mVal;
};
} // namespace
TEST(IdToObjectMap, Empty) {
IdToObjectMap<Foo> map;
EXPECT_TRUE(map.empty());
EXPECT_EQ(0U, map.size());
}
TEST(IdToObjectMap, SetIntegerRange) {
IdToObjectMap<Foo> map;
KeyType kMax = 10000;
// Add all items in the map.
for (KeyType n = 0; n < kMax; ++n) {
EXPECT_FALSE(map.set(n, new Foo(n))) << "For key " << n;
}
// Check final size.
EXPECT_EQ(static_cast<size_t>(kMax), map.size());
// Find all items in the map.
for (KeyType n = 0; n < kMax; ++n) {
EXPECT_TRUE(map.contains(n)) << "For key " << n;
Foo* foo = NULL;
EXPECT_TRUE(map.find(n, &foo)) << "For key " << n;
if (foo) {
EXPECT_EQ(static_cast<int>(n), foo->val()) << "For key " << n;
}
}
}
TEST(IdToObjectMap, RemoveAll) {
IdToObjectMap<Foo> map;
KeyType kMax = 10000;
// Add all items in the map.
for (KeyType n = 0; n < kMax; ++n) {
EXPECT_FALSE(map.set(n, new Foo(n))) << "For key " << n;
}
EXPECT_EQ(static_cast<size_t>(kMax), map.size());
for (KeyType n = 0; n < kMax; ++n) {
EXPECT_TRUE(map.remove(n)) << "For key " << n;
}
EXPECT_EQ(0U, map.size());
}
TEST(IdToObjectMap, RemoveOdd) {
IdToObjectMap<Foo> map;
KeyType kMax = 10000;
// Add all items in the map.
for (KeyType n = 0; n < kMax; ++n) {
EXPECT_FALSE(map.set(n, new Foo(n))) << "For key " << n;
}
EXPECT_EQ(static_cast<size_t>(kMax), map.size());
for (KeyType n = 0; n < kMax; ++n) {
if (n & 1) {
EXPECT_TRUE(map.remove(n)) << "For key " << n;
}
}
EXPECT_EQ(static_cast<size_t>(kMax / 2), map.size());
for (KeyType n = 0; n < kMax; ++n) {
if (n & 1) {
EXPECT_FALSE(map.contains(n)) << "For key " << n;
} else {
EXPECT_TRUE(map.contains(n)) << "For key " << n;
Foo* foo = NULL;
EXPECT_TRUE(map.find(n, &foo)) << "For key " << n;
if (foo) {
EXPECT_EQ(static_cast<int>(n), foo->val());
}
}
}
}
} // namespace emugl

View file

@ -0,0 +1,101 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/lazy_instance.h"
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# include <windows.h>
#else
# include <sched.h>
#endif
namespace emugl {
namespace internal {
typedef LazyInstanceState::AtomicType AtomicType;
#if defined(__GNUC__)
static inline void compilerBarrier() {
__asm__ __volatile__ ("" : : : "memory");
}
#else
#error "Your compiler is not supported"
#endif
#if defined(__i386__) || defined(__x86_64__) || defined(__arm__)
# define acquireBarrier() compilerBarrier()
# define releaseBarrier() compilerBarrier()
#else
# error "Your CPU is not supported"
#endif
static inline AtomicType loadAcquire(AtomicType volatile* ptr) {
AtomicType ret = *ptr;
acquireBarrier();
return ret;
}
static inline void storeRelease(AtomicType volatile* ptr, AtomicType value) {
releaseBarrier();
*ptr = value;
}
static int atomicCompareAndSwap(AtomicType volatile* ptr,
int expected,
int value) {
#ifdef _WIN32
return InterlockedCompareExchange(ptr, value, expected);
#elif defined(__GNUC__)
return __sync_val_compare_and_swap(ptr, expected, value);
#else
#error "Your compiler is not supported"
#endif
}
static void yieldThread() {
#ifdef _WIN32
::Sleep(0);
#else
sched_yield();
#endif
}
bool LazyInstanceState::inInitState() {
return loadAcquire(&mState) == STATE_INIT;
}
bool LazyInstanceState::needConstruction() {
AtomicType state = loadAcquire(&mState);
if (mState == STATE_DONE)
return false;
state = atomicCompareAndSwap(&mState, STATE_INIT, STATE_CONSTRUCTING);
if (state == STATE_INIT)
return true;
do {
yieldThread();
state = loadAcquire(&mState);
} while (state != STATE_DONE);
return false;
}
void LazyInstanceState::doneConstructing() {
storeRelease(&mState, STATE_DONE);
}
} // namespace internal
} // namespace emugl

View file

@ -0,0 +1,156 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_LAZY_INSTANCE_H
#define EMUGL_COMMON_LAZY_INSTANCE_H
#include <new>
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# include <windows.h>
#endif
namespace emugl {
namespace internal {
// A LazyInstance is a helper template that can be used to perform
// thread-safe lazy initialization of static C++ objects without forcing
// the generation of C++ static constructors in the final executable.
//
// In a nutshell, you can replace a statement like:
//
// static Foo gFoo;
//
// With:
//
// static LazyInstance<Foo> gFoo = LAZY_INSTANCE_INIT;
//
// In the first case, a hidden static C++ constructor is embedded in the
// final executable, and executed at *load* *time* to call the Foo::Foo
// constructor on the gFoo object.
//
// On the second case, gFoo will only be initialized lazily, i.e. the first
// time any code actually tries to access the variable.
//
// Note that access is slightly different, i.e.:
//
// gFoo.get() returns a reference to the lazy-initialized object.
// gFoo.ptr() returns a pointer to it.
// gFoo->Something() is equivalent to doing gFoo.ptr()->Something().
//
// 'gFoo' is stored in the .bss section and this doesn't use heap allocation.
// This class can only be used to perform lazy initialization through the
// class' default constructor. For more specialized cases, you will have
// to create a derived class, e.g.:
//
// class FoorWithDefaultParams : public Foo {
// public:
// FooWithDefaultParams() : Foo(<default-parameters>) {}
// };
//
// LazyInstance<FooWithDefaultParams> gFoo = LAZY_INSTANCE_INIT;
//
// The implementation of LazyInstance relies on atomic operations and
// POD-struct class definitions, i.e. one that doesn't have any constructor,
// destructor, virtual members, or private ones, and that can be
// zero-initialized at link time.
//
// You can also use LazyInstance<> instances as static local variables,
// e.g.:
//
// Foo* getFooSingleton() {
// static LazyInstance<Foo> sFoo = LAZY_INSTANCE_INIT;
// return sFoo.ptr();
// }
//
// This is useful on Windows which doesn't support thread-safe lazy
// initialization of static C++ local variables, or when the code is
// compiled with -fno-threadsafe-statics.
//
// This class is heavily inspired by Chromium's implementation of the
// same-named class (see $CHROMIUM/src/base/lazy_instance.h).
// Atomic state variable type. Used to ensure to synchronize concurrent
// initialization and access without incurring the full cost of a mutex
// lock/unlock.
struct LazyInstanceState {
enum {
STATE_INIT = 0,
STATE_CONSTRUCTING = 1,
STATE_DONE = 2,
};
bool inInitState();
bool needConstruction();
void doneConstructing();
#ifdef _WIN32
typedef LONG volatile AtomicType;
#else
typedef int volatile AtomicType;
#endif
volatile AtomicType mState;
};
#define LAZY_INSTANCE_STATE_INIT \
{ ::emugl::internal::LazyInstanceState::STATE_INIT }
} // namespace internal
// LazyInstance template definition, see comment above for usage
// instructions. It is crucial to make this a POD-struct compatible
// type [1].
//
// [1] http://en.wikipedia.org/wiki/Plain_Old_Data_Structures
//
template <class T>
struct LazyInstance {
bool hasInstance() const { return !mState.inInitState(); }
T& get() const { return *ptr(); }
T* ptr() const;
const T* operator->() const { return ptr(); }
T* operator->() { return ptr(); }
T& operator*() { return get(); }
// Really private, do not use.
union {
mutable internal::LazyInstanceState mState;
double mPadding;
};
mutable char mStorage[sizeof(T)];
};
// Initialization value, must resolve to all-0 to ensure the object
// instance is actually placed in the .bss
#define LAZY_INSTANCE_INIT { { LAZY_INSTANCE_STATE_INIT }, { 0 } }
template <class T>
T* LazyInstance<T>::ptr() const {
if (mState.needConstruction()) {
new (mStorage) T();
mState.doneConstructing();
}
return reinterpret_cast<T*>(mStorage);
}
} // namespace emugl
#endif // EMUGL_COMMON_LAZY_INSTANCE_H

View file

@ -0,0 +1,146 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/lazy_instance.h"
#include "emugl/common/mutex.h"
#include "emugl/common/testing/test_thread.h"
#include <gtest/gtest.h>
namespace emugl {
namespace {
class Foo {
public:
Foo() : mValue(42) {}
int get() const { return mValue; }
void set(int value) { mValue = value; }
~Foo() { mValue = 13; }
private:
int mValue;
};
class StaticCounter {
public:
StaticCounter() {
Mutex::AutoLock lock(mMutex);
mCounter++;
}
int getValue() const {
Mutex::AutoLock lock(mMutex);
return mCounter;
}
private:
static Mutex mMutex;
static int mCounter;
};
// NOTE: This introduces a static C++ constructor for this object file,
// but that's ok because a LazyInstance<Mutex> should not be used to
// test the behaviour of LazyInstance :-)
Mutex StaticCounter::mMutex;
int StaticCounter::mCounter = 0;
} // namespace
TEST(LazyInstance, HasInstance) {
LazyInstance<Foo> foo_instance = LAZY_INSTANCE_INIT;
EXPECT_FALSE(foo_instance.hasInstance());
EXPECT_FALSE(foo_instance.hasInstance());
foo_instance.ptr();
EXPECT_TRUE(foo_instance.hasInstance());
}
TEST(LazyInstance, Simple) {
LazyInstance<Foo> foo_instance = LAZY_INSTANCE_INIT;
Foo* foo1 = foo_instance.ptr();
EXPECT_TRUE(foo1);
EXPECT_EQ(42, foo_instance->get());
foo1->set(10);
EXPECT_EQ(10, foo_instance->get());
EXPECT_EQ(foo1, foo_instance.ptr());
}
// For the following test, launch 1000 threads that each try to get
// the instance pointer of a lazy instance. Then verify that they're all
// the same value.
//
// The lazy instance has a special constructor that will increment a
// global counter. This allows us to ensure that it is only called once.
//
namespace {
// The following is the shared structure between all threads.
struct MultiState {
MultiState(LazyInstance<StaticCounter>* staticCounter) :
mMutex(), mStaticCounter(staticCounter), mCount(0) {}
enum {
kMaxThreads = 1000,
};
Mutex mMutex;
LazyInstance<StaticCounter>* mStaticCounter;
size_t mCount;
void* mValues[kMaxThreads];
TestThread* mThreads[kMaxThreads];
};
// The thread function for the test below.
static void* threadFunc(void* param) {
MultiState* state = static_cast<MultiState*>(param);
Mutex::AutoLock lock(state->mMutex);
if (state->mCount < MultiState::kMaxThreads) {
state->mValues[state->mCount++] = state->mStaticCounter->ptr();
}
return NULL;
}
} // namespace
TEST(LazyInstance, MultipleThreads) {
LazyInstance<StaticCounter> counter_instance = LAZY_INSTANCE_INIT;
MultiState state(&counter_instance);
const size_t kNumThreads = MultiState::kMaxThreads;
// Create all threads.
for (size_t n = 0; n < kNumThreads; ++n) {
state.mThreads[n] = new TestThread(threadFunc, &state);
}
// Wait for their completion.
for (size_t n = 0; n < kNumThreads; ++n) {
state.mThreads[n]->join();
}
// Now check that the constructor was only called once.
EXPECT_EQ(1, counter_instance->getValue());
// Now compare all the store values, they should be the same.
StaticCounter* expectedValue = counter_instance.ptr();
for (size_t n = 0; n < kNumThreads; ++n) {
EXPECT_EQ(expectedValue, state.mValues[n]) << "For thread " << n;
}
for (size_t n = 0; n < kNumThreads; ++n) {
delete state.mThreads[n];
}
}
} // namespace emugl

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "emugl/common/logging.h"
void default_logger(const char* fmt, ...) { }
logger_t emugl_logger = default_logger;
logger_t emugl_cxt_logger = default_logger;
void set_emugl_logger(logger_t f) {
if (!f) {
emugl_logger = default_logger;
} else {
emugl_logger = f;
}
}
void set_emugl_cxt_logger(logger_t f) {
if (!f) {
emugl_cxt_logger = default_logger;
} else {
emugl_cxt_logger = f;
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
typedef void (*logger_t)(const char* fmt, ...);
extern logger_t emugl_logger;
extern logger_t emugl_cxt_logger;
void set_emugl_logger(logger_t f);
void set_emugl_cxt_logger(logger_t f);
#define GL_LOGGING 1
#if GL_LOGGING
#define GL_LOG(...) do { \
emugl_logger(__VA_ARGS__); \
} while (0)
#define GL_CXT_LOG(...) do { \
emugl_cxt_logger(__VA_ARGS__); \
} while (0)
#else
#define GL_LOG(...) 0
#endif

View file

@ -0,0 +1,64 @@
// Copyright 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/message_channel.h"
namespace emugl {
MessageChannelBase::MessageChannelBase(size_t capacity) :
mPos(0U),
mCount(0U),
mCapacity(capacity),
mLock(),
mCanRead(),
mCanWrite() {}
MessageChannelBase::~MessageChannelBase() {}
size_t MessageChannelBase::beforeWrite() {
mLock.lock();
while (mCount >= mCapacity) {
mCanWrite.wait(&mLock);
}
size_t result = mPos + mCount;
if (result >= mCapacity) {
result -= mCapacity;
}
return result;
}
void MessageChannelBase::afterWrite() {
mCount++;
mCanRead.signal();
mLock.unlock();
}
size_t MessageChannelBase::beforeRead() {
mLock.lock();
while (mCount == 0) {
mCanRead.wait(&mLock);
}
return mPos;
}
void MessageChannelBase::afterRead() {
if (++mPos == mCapacity) {
mPos = 0U;
}
mCount--;
mCanWrite.signal();
mLock.unlock();
}
} // namespace emugl

View file

@ -0,0 +1,98 @@
// Copyright 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_MESSAGE_CHANNEL_H
#define EMUGL_COMMON_MESSAGE_CHANNEL_H
#include "emugl/common/condition_variable.h"
#include "emugl/common/mutex.h"
#include <stddef.h>
namespace emugl {
// Base non-templated class used to reduce the amount of template
// specialization.
class MessageChannelBase {
public:
// Constructor. |capacity| is the buffer capacity in messages.
MessageChannelBase(size_t capacity);
// Destructor.
~MessageChannelBase();
protected:
// Call this method in the sender thread before writing a new message.
// This returns the position of the available slot in the message array
// where to copy the new fixed-size message. After the copy, call
// afterWrite().
size_t beforeWrite();
// To be called after beforeWrite() and copying a new fixed-size message
// into the array. This signal the receiver thread that there is a new
// incoming message.
void afterWrite();
// Call this method in the receiver thread before reading a new message.
// This returns the position in the message array where the new message
// can be read. Caller must process the message, then call afterRead().
size_t beforeRead();
// To be called in the receiver thread after beforeRead() and processing
// the corresponding message.
void afterRead();
private:
size_t mPos;
size_t mCount;
size_t mCapacity;
Mutex mLock;
ConditionVariable mCanRead;
ConditionVariable mCanWrite;
};
// Helper class used to implement an uni-directional IPC channel between
// two threads. The channel can be used to send fixed-size messages of type
// |T|, with an internal buffer size of |CAPACITY| items. All calls are
// blocking.
//
// Usage is pretty straightforward:
//
// - From the sender thread, call send(msg);
// - From the receiver thread, call receive(&msg);
//
template <typename T, size_t CAPACITY>
class MessageChannel : public MessageChannelBase {
public:
MessageChannel() : MessageChannelBase(CAPACITY) {}
void send(const T& msg) {
size_t pos = beforeWrite();
mItems[pos] = msg;
afterWrite();
}
void receive(T* msg) {
size_t pos = beforeRead();
*msg = mItems[pos];
afterRead();
}
private:
T mItems[CAPACITY];
};
} // namespace emugl
#endif // EMUGL_COMMON_MESSAGE_CHANNEL_H

View file

@ -0,0 +1,101 @@
// Copyright 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/message_channel.h"
#include "emugl/common/testing/test_thread.h"
#include <gtest/gtest.h>
#include <string>
namespace emugl {
namespace {
struct PingPongState {
MessageChannel<std::string, 3U> in;
MessageChannel<std::string, 3U> out;
};
void* pingPongFunction(void* param) {
for (;;) {
PingPongState* s = static_cast<PingPongState*>(param);
std::string str;
s->in.receive(&str);
s->out.send(str);
if (str == "quit") {
break;
}
}
return 0;
}
} // namespace
TEST(MessageChannel, SingleThreadWithInt) {
MessageChannel<int, 3U> channel;
channel.send(1);
channel.send(2);
channel.send(3);
int ret;
channel.receive(&ret);
EXPECT_EQ(1, ret);
channel.receive(&ret);
EXPECT_EQ(2, ret);
channel.receive(&ret);
EXPECT_EQ(3, ret);
}
TEST(MessageChannel, SingleThreadWithStdString) {
MessageChannel<std::string, 5U> channel;
channel.send(std::string("foo"));
channel.send(std::string("bar"));
channel.send(std::string("zoo"));
std::string str;
channel.receive(&str);
EXPECT_STREQ("foo", str.c_str());
channel.receive(&str);
EXPECT_STREQ("bar", str.c_str());
channel.receive(&str);
EXPECT_STREQ("zoo", str.c_str());
}
TEST(MessageChannel, TwoThreadsPingPong) {
PingPongState state;
TestThread* thread = new TestThread(pingPongFunction, &state);
std::string str;
const size_t kCount = 100;
for (size_t n = 0; n < kCount; ++n) {
state.in.send(std::string("foo"));
state.in.send(std::string("bar"));
state.in.send(std::string("zoo"));
state.out.receive(&str);
EXPECT_STREQ("foo", str.c_str());
state.out.receive(&str);
EXPECT_STREQ("bar", str.c_str());
state.out.receive(&str);
EXPECT_STREQ("zoo", str.c_str());
}
state.in.send(std::string("quit"));
state.out.receive(&str);
EXPECT_STREQ("quit", str.c_str());
thread->join();
}
} // namespace emugl

View file

@ -0,0 +1,95 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_MUTEX_H
#define EMUGL_MUTEX_H
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# include <windows.h>
#else
# include <pthread.h>
#endif
namespace emugl {
class ConditionVariable;
// Simple wrapper class for mutexes.
class Mutex {
public:
// Constructor.
Mutex() {
#ifdef _WIN32
::InitializeCriticalSection(&mLock);
#else
::pthread_mutex_init(&mLock, NULL);
#endif
}
// Destructor.
~Mutex() {
#ifdef _WIN32
::DeleteCriticalSection(&mLock);
#else
::pthread_mutex_destroy(&mLock);
#endif
}
// Acquire the mutex.
void lock() {
#ifdef _WIN32
::EnterCriticalSection(&mLock);
#else
::pthread_mutex_lock(&mLock);
#endif
}
// Release the mutex.
void unlock() {
#ifdef _WIN32
::LeaveCriticalSection(&mLock);
#else
::pthread_mutex_unlock(&mLock);
#endif
}
// Helper class to lock / unlock a mutex automatically on scope
// entry and exit.
class AutoLock {
public:
AutoLock(Mutex& mutex) : mMutex(&mutex) {
mMutex->lock();
}
~AutoLock() {
mMutex->unlock();
}
private:
Mutex* mMutex;
};
private:
#ifdef _WIN32
CRITICAL_SECTION mLock;
#else
friend class ConditionVariable;
pthread_mutex_t mLock;
#endif
};
} // namespace emugl
#endif // EMUGL_MUTEX_H

View file

@ -0,0 +1,108 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/mutex.h"
#include "emugl/common/testing/test_thread.h"
#include <gtest/gtest.h>
namespace emugl {
// Check that construction and destruction doesn't crash.
TEST(Mutex, ConstructionDestruction) {
Mutex lock;
}
// Check that a simple lock + unlock works.
TEST(Mutex, LockUnlock) {
Mutex lock;
lock.lock();
lock.unlock();
}
// Check that AutoLock compiles and doesn't crash.
TEST(Mutex, AutoLock) {
Mutex mutex;
Mutex::AutoLock lock(mutex);
}
// Wrapper class for threads. Does not use emugl::Thread intentionally.
// Common data type used by the following tests below.
struct ThreadParams {
ThreadParams() : mutex(), counter(0) {}
Mutex mutex;
int counter;
};
// This thread function uses Mutex::lock/unlock to synchronize a counter
// increment operation.
static void* threadFunction(void* param) {
ThreadParams* p = static_cast<ThreadParams*>(param);
p->mutex.lock();
p->counter++;
p->mutex.unlock();
return NULL;
}
TEST(Mutex, Synchronization) {
const size_t kNumThreads = 2000;
TestThread* threads[kNumThreads];
ThreadParams p;
// Create and launch all threads.
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n] = new TestThread(threadFunction, &p);
}
// Wait until their completion.
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n]->join();
delete threads[n];
}
EXPECT_EQ(static_cast<int>(kNumThreads), p.counter);
}
// This thread function uses a Mutex::AutoLock to protect the counter.
static void* threadAutoLockFunction(void* param) {
ThreadParams* p = static_cast<ThreadParams*>(param);
Mutex::AutoLock lock(p->mutex);
p->counter++;
return NULL;
}
TEST(Mutex, AutoLockSynchronization) {
const size_t kNumThreads = 2000;
TestThread* threads[kNumThreads];
ThreadParams p;
// Create and launch all threads.
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n] = new TestThread(threadAutoLockFunction, &p);
}
// Wait until their completion.
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n]->join();
delete threads[n];
}
EXPECT_EQ(static_cast<int>(kNumThreads), p.counter);
}
} // namespace emugl

View file

@ -0,0 +1,150 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/pod_vector.h"
#include <stdlib.h>
#include <string.h>
#define USE_MALLOC_USABLE_SIZE 0
namespace emugl {
static inline void swapPointers(char** p1, char** p2) {
char* tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
PodVectorBase::PodVectorBase(const PodVectorBase& other) {
initFrom(other.begin(), other.byteSize());
}
PodVectorBase& PodVectorBase::operator=(const PodVectorBase& other) {
initFrom(other.begin(), other.byteSize());
return *this;
}
PodVectorBase::~PodVectorBase() {
if (mBegin) {
// Sanity.
::memset(mBegin, 0xee, byteSize());
::free(mBegin);
mBegin = NULL;
mEnd = NULL;
mLimit = NULL;
}
}
void PodVectorBase::initFrom(const void* from, size_t fromLen) {
if (!fromLen || !from) {
mBegin = NULL;
mEnd = NULL;
mLimit = NULL;
} else {
mBegin = static_cast<char*>(::malloc(fromLen));
mEnd = mLimit = mBegin + fromLen;
::memcpy(mBegin, from, fromLen);
}
}
void PodVectorBase::assignFrom(const PodVectorBase& other) {
resize(other.byteSize(), 1U);
::memmove(begin(), other.begin(), byteSize());
}
void PodVectorBase::resize(size_t newSize, size_t itemSize) {
const size_t kMaxSize = maxItemCapacity(itemSize);
size_t oldCapacity = itemCapacity(itemSize);
const size_t kMinCapacity = 256 / itemSize;
if (newSize < oldCapacity) {
// Only shrink if the new size is really small.
if (newSize < oldCapacity / 2 && oldCapacity > kMinCapacity) {
reserve(newSize, itemSize);
}
} else if (newSize > oldCapacity) {
size_t newCapacity = oldCapacity;
while (newCapacity < newSize) {
size_t newCapacity2 = newCapacity + (newCapacity >> 2) + 8;
if (newCapacity2 < newCapacity || newCapacity > kMaxSize) {
newCapacity = kMaxSize;
} else {
newCapacity = newCapacity2;
}
}
reserve(newCapacity, itemSize);
}
mEnd = mBegin + newSize * itemSize;
}
void PodVectorBase::reserve(size_t newSize, size_t itemSize) {
if (newSize == 0) {
::free(mBegin);
mBegin = NULL;
mEnd = NULL;
mLimit = NULL;
return;
}
size_t oldByteSize = byteSize();
size_t newByteCapacity = newSize * itemSize;
char* newBegin = static_cast<char*>(::realloc(mBegin, newByteCapacity));
mBegin = newBegin;
mEnd = newBegin + oldByteSize;
#if USE_MALLOC_USABLE_SIZE
size_t usableSize = malloc_usable_size(mBegin);
if (usableSize > newByteCapacity) {
newByteCapacity = usableSize - (usableSize % itemSize);
}
#endif
mLimit = newBegin + newByteCapacity;
// Sanity.
if (newByteCapacity > oldByteSize) {
::memset(mBegin + oldByteSize, 0, newByteCapacity - oldByteSize);
}
}
void PodVectorBase::removeAt(size_t itemPos, size_t itemSize) {
size_t count = itemCount(itemSize);
if (itemPos < count) {
size_t pos = itemPos * itemSize;
::memmove(mBegin + pos,
mBegin + pos + itemSize,
byteSize() - pos - itemSize);
resize(count - 1U, itemSize);
}
}
void* PodVectorBase::insertAt(size_t itemPos, size_t itemSize) {
size_t count = this->itemCount(itemSize);
resize(count + 1, itemSize);
size_t pos = itemPos * itemSize;
if (itemPos < count) {
::memmove(mBegin + pos + itemSize,
mBegin + pos,
count * itemSize - pos);
// Sanity to avoid copying pointers and other bad stuff.
::memset(mBegin + pos, 0, itemSize);
}
return mBegin + pos;
}
void PodVectorBase::swapAll(PodVectorBase* other) {
swapPointers(&mBegin, &other->mBegin);
swapPointers(&mEnd, &other->mEnd);
swapPointers(&mLimit, &other->mLimit);
}
} // namespace emugl

View file

@ -0,0 +1,265 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_POD_VECTOR_H
#define EMUGL_COMMON_POD_VECTOR_H
#include <stddef.h>
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS 1
#endif
#include <stdint.h>
#ifndef SIZE_MAX
#error "You must define __STDC_LIMIT_MACROS before including <stddint.h>"
#endif
namespace emugl {
// A PodVector is a templated vector-like type that is used to store
// POD-struct compatible items only. This allows the implementation to
// use ::memmove() to move items, and also malloc_usable_size() to
// determine the best capacity.
//
// std::vector<> is capable of doing this in theory, using horrible
// templating tricks that make error messages very difficult to
// understand.
//
// Note that a PodVector can be used to store items that contain pointers,
// as long as these do not point to items in the same container.
//
// The PodVector provides methods that also follow the std::vector<>
// conventions, i.e. push_back() is an alias for append().
//
// NOTE: This is a re-implementation of
// external/qemu/android/base/containers/PodVector.h for emugl.
// PodVectorBase is a base, non-templated, implementation class that all
// PodVector instances derive from. This is used to reduce template
// specialization. Do not use directly, i..e it's an implementation detail.
class PodVectorBase {
protected:
PodVectorBase() : mBegin(NULL), mEnd(NULL), mLimit(NULL) {}
explicit PodVectorBase(const PodVectorBase& other);
PodVectorBase& operator=(const PodVectorBase& other);
~PodVectorBase();
bool empty() const { return mEnd == mBegin; }
size_t byteSize() const { return mEnd - mBegin; }
size_t byteCapacity() const { return mLimit - mBegin; }
void* begin() { return mBegin; }
const void* begin() const { return mBegin; }
void* end() { return mEnd; }
const void* end() const { return mEnd; }
void* itemAt(size_t pos, size_t itemSize) {
return mBegin + pos * itemSize;
}
const void* itemAt(size_t pos, size_t itemSize) const {
return mBegin + pos * itemSize;
}
void assignFrom(const PodVectorBase& other);
inline size_t itemCount(size_t itemSize) const {
return byteSize() / itemSize;
}
inline size_t itemCapacity(size_t itemSize) const {
return byteCapacity() / itemSize;
}
inline size_t maxItemCapacity(size_t itemSize) const {
return SIZE_MAX / itemSize;
}
void resize(size_t newSize, size_t itemSize);
void reserve(size_t newSize, size_t itemSize);
void removeAt(size_t index, size_t itemSize);
void* insertAt(size_t index, size_t itemSize);
void swapAll(PodVectorBase* other);
char* mBegin;
char* mEnd;
char* mLimit;
private:
void initFrom(const void* from, size_t fromLen);
};
// A PodVector<T> holds a vector (dynamically resizable array) or items
// that must be POD-struct compatible (i.e. they cannot have constructors,
// destructors, or virtual members). This allows the implementation to be
// small, fast and efficient, memory-wise.
//
// If you want to implement a vector of C++ objects, consider using
// std::vector<> instead, but keep in mind that this implies a non-trivial
// cost when appending, inserting, removing items in the collection.
//
template <typename T>
class PodVector : public PodVectorBase {
public:
// Default constructor for an empty PodVector<T>
PodVector() : PodVectorBase() {}
// Copy constructor. This copies all items from |other| into
// the new instance with ::memmove().
PodVector(const PodVector& other) : PodVectorBase(other) {}
// Assignment operator.
PodVector& operator=(const PodVector& other) {
this->assignFrom(other);
return *this;
}
// Destructor, this simply releases the internal storage block that
// holds all the items, but doesn't touch them otherwise.
~PodVector() {}
// Return true iff the PodVector<T> instance is empty, i.e. does not
// have any items.
bool empty() const { return PodVectorBase::empty(); }
// Return the number of items in the current PodVector<T> instance.
size_t size() const { return PodVectorBase::itemCount(sizeof(T)); }
// Return the current capacity in the current PodVector<T> instance.
// Do not use directly, except if you know what you're doing. Try to
// use resize() or reserve() instead.
size_t capacity() const {
return PodVectorBase::itemCapacity(sizeof(T));
}
// Return the maximum capacity of any PodVector<T> instance.
static inline size_t maxCapacity() { return SIZE_MAX / sizeof(T); }
// Resize the vector to ensure it can hold |newSize|
// items. This may or may not call reserve() under the hood.
// It's a fatal error to try to resize above maxCapacity().
void resize(size_t newSize) {
PodVectorBase::resize(newSize, sizeof(T));
}
// Resize the vector's storage array to ensure that it can hold at
// least |newSize| items. It's a fatal error to try to resize above
// maxCapacity().
void reserve(size_t newSize) {
PodVectorBase::reserve(newSize, sizeof(T));
}
// Return a pointer to the first item in the vector. This is only
// valid until the next call to any function that changes the size
// or capacity of the vector. Can be NULL if the vector is empty.
T* begin() {
return reinterpret_cast<T*>(PodVectorBase::begin());
}
// Return a constant pointer to the first item in the vector. This is
// only valid until the next call to any function that changes the
// size of capacity of the vector.
const T* begin() const {
return reinterpret_cast<T*>(PodVectorBase::begin());
}
// Return a pointer past the last item in the vector. I.e. if the
// result is not NULL, then |result - 1| points to the last item.
// Can be NULL if the vector is empty.
T* end() {
return reinterpret_cast<T*>(PodVectorBase::end());
}
// Return a constant pointer past the last item in the vector. I.e. if
// the result is not NULL, then |result - 1| points to the last item.
// Can be NULL if the vector is empty.
const T* end() const {
return reinterpret_cast<T*>(PodVectorBase::end());
}
// Returns a reference to the item a position |index| in the vector.
// It may be a fatal error to access an out-of-bounds position.
T& operator[](size_t index) {
return *reinterpret_cast<T*>(
PodVectorBase::itemAt(index, sizeof(T)));
}
const T& operator[](size_t index) const {
return *reinterpret_cast<const T*>(
PodVectorBase::itemAt(index, sizeof(T)));
}
// Increase the vector's size by 1, then move all items past a given
// position to the right. Return the position of the insertion point
// to let the caller copy the content it desires there. It's preferrable
// to use insert() directly, which will do the item copy for you.
T* emplace(size_t index) {
return reinterpret_cast<T*>(
PodVectorBase::insertAt(index, sizeof(T)));
}
// Insert an item at a given position. |index| is the insertion position
// which must be between 0 and size() included, or a fatal error may
// occur. |item| is a reference to an item to copy into the array,
// byte-by-byte.
void insert(size_t index, const T& item) {
*(this->emplace(index)) = item;
}
// Prepend an item at the start of a vector. This moves all vector items
// to the right, and is thus costly. |item| is a reference to an item
// to copy to the start of the vector.
void prepend(const T& item) {
*(this->emplace(0U)) = item;
}
// Append an item at the end of a vector. Specialized version of insert()
// which always uses size() as the insertion position.
void append(const T& item) {
*(this->emplace(this->size())) = item;
}
// Remove the item at a given position. |index| is the position of the
// item to delete. It must be between 0 and size(), included, or
// a fatal error may occur. Deleting the item at position size()
// doesn't do anything.
void remove(size_t index) {
PodVectorBase::removeAt(index, sizeof(T));
}
void swap(PodVector* other) {
PodVectorBase::swapAll(other);
}
// Compatibility methods for std::vector<>
void push_back(const T& item) { append(item); }
void pop() { remove(0U); }
typedef T* iterator;
typedef const T* const_iterator;
};
} // namespace emugl
#endif // EMUGL_COMMON_POD_VECTOR_H

View file

@ -0,0 +1,127 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/pod_vector.h"
#include <gtest/gtest.h>
namespace emugl {
static int hashIndex(size_t n) {
return static_cast<int>(((n >> 14) * 13773) + (n * 51));
}
TEST(PodVector, Empty) {
PodVector<int> v;
EXPECT_TRUE(v.empty());
EXPECT_EQ(0U, v.size());
}
TEST(PodVector, AppendOneItem) {
PodVector<int> v;
v.append(10234);
EXPECT_FALSE(v.empty());
EXPECT_EQ(1U, v.size());
EXPECT_EQ(10234, v[0]);
}
TEST(PodVector, AppendLotsOfItems) {
PodVector<int> v;
const size_t kMaxCount = 10000;
for (size_t n = 0; n < kMaxCount; ++n) {
v.append(hashIndex(n));
}
EXPECT_EQ(kMaxCount, v.size());
for (size_t n = 0; n < kMaxCount; ++n) {
EXPECT_EQ(hashIndex(n), v[n]) << "At index " << n;
}
}
TEST(PodVector, RemoveFrontItems) {
PodVector<int> v;
const size_t kMaxCount = 100;
for (size_t n = 0; n < kMaxCount; ++n) {
v.append(hashIndex(n));
}
EXPECT_EQ(kMaxCount, v.size());
for (size_t n = 0; n < kMaxCount; ++n) {
EXPECT_EQ(hashIndex(n), v[0]) << "At index " << n;
v.remove(0U);
EXPECT_EQ(kMaxCount - n - 1U, v.size()) << "At index " << n;
}
}
TEST(PodVector, PrependItems) {
PodVector<int> v;
const size_t kMaxCount = 100;
for (size_t n = 0; n < kMaxCount; ++n) {
v.prepend(hashIndex(n));
}
EXPECT_EQ(kMaxCount, v.size());
for (size_t n = 0; n < kMaxCount; ++n) {
EXPECT_EQ(hashIndex(kMaxCount - n - 1), v[n]) << "At index " << n;
}
}
TEST(PodVector, ResizeExpands) {
PodVector<int> v;
const size_t kMaxCount = 100;
const size_t kMaxCount2 = 10000;
for (size_t n = 0; n < kMaxCount; ++n) {
v.append(hashIndex(n));
}
EXPECT_EQ(kMaxCount, v.size());
v.resize(kMaxCount2);
EXPECT_EQ(kMaxCount2, v.size());
for (size_t n = 0; n < kMaxCount; ++n) {
EXPECT_EQ(hashIndex(n), v[n]) << "At index " << n;
}
}
TEST(PodVector, ResizeTruncates) {
PodVector<int> v;
const size_t kMaxCount = 10000;
const size_t kMaxCount2 = 10;
for (size_t n = 0; n < kMaxCount; ++n) {
v.append(hashIndex(n));
}
EXPECT_EQ(kMaxCount, v.size());
v.resize(kMaxCount2);
EXPECT_EQ(kMaxCount2, v.size());
for (size_t n = 0; n < kMaxCount2; ++n) {
EXPECT_EQ(hashIndex(n), v[n]) << "At index " << n;
}
}
TEST(PodVector, AssignmentOperator) {
PodVector<int> v1;
const size_t kMaxCount = 10000;
for (size_t n = 0; n < kMaxCount; ++n) {
v1.append(hashIndex(n));
}
EXPECT_EQ(kMaxCount, v1.size());
PodVector<int> v2;
v2 = v1;
v1.reserve(0);
EXPECT_EQ(kMaxCount, v2.size());
for (size_t n = 0; n < kMaxCount; ++n) {
EXPECT_EQ(hashIndex(n), v2[n]) << "At index " << n;
}
}
} // namespace emugl

View file

@ -0,0 +1,27 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_SCOPED_POINTER_VECTOR_H
#define EMUGL_COMMON_SCOPED_POINTER_VECTOR_H
namespace emugl {
template <class T>
class ScopedPointerVector {
ScopedPointerVector
};
} // namespace emugl
#endif // EMUGL_COMMON_SCOPED_POINTER_VECTOR_H

View file

@ -0,0 +1,169 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/shared_library.h"
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#ifndef _WIN32
#include <dlfcn.h>
#include <stdlib.h>
#endif
namespace emugl {
// static
SharedLibrary* SharedLibrary::open(const char* libraryName) {
char error[1];
return open(libraryName, error, sizeof(error));
}
#ifdef _WIN32
// static
SharedLibrary* SharedLibrary::open(const char* libraryName,
char* error,
size_t errorSize) {
HMODULE lib = LoadLibrary(libraryName);
if (lib) {
return new SharedLibrary(lib);
}
if (errorSize == 0) {
return NULL;
}
// Convert error into human-readable message.
DWORD errorCode = ::GetLastError();
LPSTR message = NULL;
size_t messageLen = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR) &message,
0,
NULL);
int ret = snprintf(error, errorSize, "%.*s", (int)messageLen, message);
if (ret < 0 || ret == static_cast<int>(errorSize)) {
// snprintf() on Windows doesn't behave as expected by C99,
// this path is to ensure that the result is always properly
// zero-terminated.
ret = static_cast<int>(errorSize - 1);
error[ret] = '\0';
}
// Remove any trailing \r\n added by FormatMessage
if (ret > 0 && error[ret - 1] == '\n') {
error[--ret] = '\0';
}
if (ret > 0 && error[ret - 1] == '\r') {
error[--ret] = '\0';
}
return NULL;
}
SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {}
SharedLibrary::~SharedLibrary() {
if (mLib) {
FreeLibrary(mLib);
}
}
SharedLibrary::FunctionPtr SharedLibrary::findSymbol(
const char* symbolName) const {
if (!mLib || !symbolName) {
return NULL;
}
return reinterpret_cast<FunctionPtr>(
GetProcAddress(mLib, symbolName));
}
#else // !_WIN32
// static
SharedLibrary* SharedLibrary::open(const char* libraryName,
char* error,
size_t errorSize) {
const char* libPath = libraryName;
char* path = NULL;
const char* libBaseName = strrchr(libraryName, '/');
if (!libBaseName) {
libBaseName = libraryName;
}
if (!strchr(libBaseName, '.')) {
// There is no extension in this library name, so append one.
#ifdef __APPLE__
static const char kDllExtension[] = ".dylib";
#else
static const char kDllExtension[] = ".so";
#endif
size_t pathLen = strlen(libraryName) + sizeof(kDllExtension);
path = static_cast<char*>(malloc(pathLen));
snprintf(path, pathLen, "%s%s", libraryName, kDllExtension);
libPath = path;
}
dlerror(); // clear error.
#ifdef __APPLE__
// On OSX, some libraries don't include an extension (notably OpenGL)
// On OSX we try to open |libraryName| first. If that doesn't exist,
// we try |libraryName|.dylib
void* lib = dlopen(libraryName, RTLD_NOW);
if (lib == NULL) {
lib = dlopen(libPath, RTLD_NOW);
}
#else
void* lib = dlopen(libPath, RTLD_NOW);
#endif
if (path) {
free(path);
}
if (lib) {
return new SharedLibrary(lib);
}
snprintf(error, errorSize, "%s", dlerror());
return NULL;
}
SharedLibrary::SharedLibrary(HandleType lib) : mLib(lib) {}
SharedLibrary::~SharedLibrary() {
if (mLib) {
dlclose(mLib);
}
}
SharedLibrary::FunctionPtr SharedLibrary::findSymbol(
const char* symbolName) const {
if (!mLib || !symbolName) {
return NULL;
}
return reinterpret_cast<FunctionPtr>(dlsym(mLib, symbolName));
}
#endif // !_WIN32
} // namespace emugl

View file

@ -0,0 +1,106 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_SHARED_LIBRARY_H
#define EMUGL_COMMON_SHARED_LIBRARY_H
#include <stddef.h>
#ifdef _WIN32
#include <windows.h>
#endif
namespace emugl {
// A class used to open a platform-specific shared library, and probe
// it for symbols. Usage is the following:
//
// // Open the library.
// SharedLibrary* library = SharedLibrary::open("libFoo");
// if (!library) {
// ... could not find / open library!
// }
//
// //Probe for function symbol.
// FunctionPtr my_func = library->findSymbol("my_func");
//
// // Closes library/
// delete library;
//
class SharedLibrary {
public:
// Open a given library. If |libraryName| has no extension, a
// platform-appropriate extension is added and that path is opened.
// If the |libraryName| has an extension, that form is opened.
//
// On OSX, some libraries don't include an extension (notably OpenGL)
// On OSX we try to open |libraryName| first. If that doesn't exist,
// we try |libraryName|.dylib
//
// On success, returns a new SharedLibrary instance that must be
// deleted by the caller.
static SharedLibrary* open(const char* libraryName);
// A variant of open() that can report a human-readable error if loading
// the library fails. |error| is a caller-provided buffer of |errorSize|
// bytes that will be filled with snprintf() and always zero terminated.
//
// On success, return a new SharedLibrary instance, and do not touch
// the content of |error|. On failure, return NULL, and sets the content
// of |error|.
static SharedLibrary* open(const char* libraryName,
char* error,
size_t errorSize);
// Closes an existing SharedLibrary instance.
~SharedLibrary();
// Generic function pointer type, for values returned by the
// findSymbol() method.
typedef void (*FunctionPtr)(void);
// Probe a given SharedLibrary instance to find a symbol named
// |symbolName| in it. Return its address as a FunctionPtr, or
// NULL if the symbol is not found.
FunctionPtr findSymbol(const char* symbolName) const;
private:
#ifdef _WIN32
typedef HMODULE HandleType;
#else
typedef void* HandleType;
#endif
// Constructor intentionally hidden.
SharedLibrary(HandleType);
HandleType mLib;
};
// Macro to compose emugl shared library name under various OS and bitness
// eg.
// on x86_64, EMUGL_LIBNAME("foo") --> "lib64foo"
#if defined(__x86_64__)
# define EMUGL_LIBNAME(name) "lib64" name
#elif defined(__i386__)
# define EMUGL_LIBNAME(name) "lib" name
#else
/* This header is included by target w/o using EMUGL_LIBNAME(). Don't #error, leave it undefined */
# define EMUGL_LIBNAME(name) "lib" name
#endif
} // namespace emugl
#endif // EMUGL_COMMON_SHARED_LIBRARY_H

View file

@ -0,0 +1,177 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/shared_library.h"
#include <gtest/gtest.h>
#include <string>
#include <limits.h>
#include <string.h>
// Hack to get the current executable's full path.
namespace testing {
namespace internal {
extern std::string g_executable_path;
} // namespace internal
} // namespace testing
namespace emugl {
namespace {
// Return the name/path of the test shared library to load.
// Note that this doesn't include a platform-specific extension.
// This assumes that the test shared library is under the lib/ sub-directory
// of the current executable's path!
std::string GetTestLibraryName() {
#ifdef __x86_64__
static const char kLibraryPrefix[] = "lib64";
static const char kSubDir[] = "lib64/";
#else
static const char kLibraryPrefix[] = "lib";
static const char kSubDir[] = "lib/";
#endif
static const char kTestLibrarySuffix[] = "emugl_test_shared_library";
const char* exec_path = testing::internal::g_executable_path.c_str();
#ifdef _WIN32
const char* p = strrchr(exec_path, '/');
const char* p2 = strrchr(exec_path, '\\');
if (p2) {
if (!p || p2 > p) {
p = p2;
}
}
#else
const char* p = strrchr(exec_path, '/');
#endif
std::string path;
if (!p) {
path = "./";
} else {
path = std::string(exec_path, p - exec_path + 1U);
}
path += kSubDir;
path += kLibraryPrefix;
path += kTestLibrarySuffix;
printf("Library path: %s\n", path.c_str());
return path;
}
class SharedLibraryTest : public testing::Test {
public:
SharedLibraryTest() {
// Locate the shared library
mLibraryPath = GetTestLibraryName();
}
~SharedLibraryTest() {}
const char* library_path() const { return mLibraryPath.c_str(); }
private:
std::string mLibraryPath;
};
class ScopedSharedLibrary {
public:
explicit ScopedSharedLibrary(const SharedLibrary* lib) : mLib(lib) {}
~ScopedSharedLibrary() {
delete mLib;
}
const SharedLibrary* get() const { return mLib; }
const SharedLibrary* operator->() { return mLib; }
void release() {
delete mLib;
mLib = NULL;
}
private:
const SharedLibrary* mLib;
};
} // namespace
TEST_F(SharedLibraryTest, Open) {
ScopedSharedLibrary lib(SharedLibrary::open(library_path()));
EXPECT_TRUE(lib.get());
}
TEST_F(SharedLibraryTest, OpenFailureWithError) {
char error[256];
error[0] = '\0';
SharedLibrary* lib = SharedLibrary::open("/tmp/does/not/exists",
error,
sizeof(error));
EXPECT_FALSE(lib);
EXPECT_TRUE(error[0])
<< "Could not get error string when failing to load library";
printf("Expected library load failure: [%s]\n", error);
}
TEST_F(SharedLibraryTest, OpenLibraryWithExtension) {
std::string path = library_path();
// test extension append
ScopedSharedLibrary libNoExtension(SharedLibrary::open(path.c_str()));
EXPECT_TRUE(libNoExtension.get());
libNoExtension.release();
#ifdef _WIN32
path += ".dll";
#elif defined(__APPLE__)
// try to open the library without an extension
path += ".dylib";
#else
path += ".so";
#endif
// test open with prepended extension
ScopedSharedLibrary lib(SharedLibrary::open(path.c_str()));
EXPECT_TRUE(lib.get());
}
#ifdef __APPLE__
TEST_F(SharedLibraryTest, OpenLibraryWithoutExtension) {
const char* library = "/System/Library/Frameworks/OpenGL.framework/OpenGL";
ScopedSharedLibrary lib(SharedLibrary::open(library));
EXPECT_TRUE(lib.get());
}
#endif
TEST_F(SharedLibraryTest, FindSymbol) {
ScopedSharedLibrary lib(SharedLibrary::open(library_path()));
EXPECT_TRUE(lib.get());
if (lib.get()) {
typedef int (*FooFunction)(void);
FooFunction foo_func = reinterpret_cast<FooFunction>(
lib->findSymbol("foo_function"));
EXPECT_TRUE(foo_func);
EXPECT_EQ(42, (*foo_func)());
}
}
} // namespace emugl

View file

@ -0,0 +1,113 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/smart_ptr.h"
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
namespace emugl {
// Thread-safe atomic reference-counting type.
class RefCount {
public:
RefCount() : mCount(1) {}
~RefCount() {}
// Technically not thread-safe, use only for testing.
int count() const { return (int)mCount; }
void increment() {
#ifdef _WIN32
InterlockedIncrement(&mCount);
#else
__sync_fetch_and_add(&mCount, 1);
#endif
}
bool decrement() {
#ifdef _WIN32
return InterlockedDecrement(&mCount) == 0;
#else
return __sync_add_and_fetch(&mCount, -1) == 0;
#endif
}
private:
#ifdef _WIN32
LONG mCount;
#else
int mCount;
#endif
};
// SmartPtrBase implementation.
SmartPtrBase::SmartPtrBase(void* ptr) : mPtr(ptr), mRefCount(NULL) {
if (mPtr)
mRefCount = new RefCount();
}
SmartPtrBase::SmartPtrBase(const SmartPtrBase& other)
: mPtr(other.mPtr), mRefCount(other.mRefCount) {
if (mRefCount)
mRefCount->increment();
}
int SmartPtrBase::getRefCount() const {
return mRefCount ? mRefCount->count() : 0;
}
void SmartPtrBase::addRef() {
if (mRefCount)
mRefCount->increment();
}
void* SmartPtrBase::copyFrom(const SmartPtrBase& other) {
void* old_ptr = release();
mPtr = other.mPtr;
mRefCount = other.mRefCount;
if (mRefCount)
mRefCount->increment();
return old_ptr;
}
void* SmartPtrBase::release() {
void* old_ptr = mPtr;
RefCount* old_refcount = mRefCount;
if (old_refcount) {
mPtr = NULL;
mRefCount = NULL;
if (old_refcount->decrement()) {
delete old_refcount;
return old_ptr;
}
}
return NULL;
}
} // namespace emugl

View file

@ -0,0 +1,150 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_SMART_PTR_H
#define EMUGL_SMART_PTR_H
#include <stddef.h>
namespace emugl {
// Hidden atomic ref-counting implementation.
class RefCount;
// Base class for all templated SmartPtr<> instances. Reduces
// template expansion and code. Consider this to be an implementation
// detail of SmartPtr<>, so don't rely on anything here.
class SmartPtrBase {
public:
// Defrault constructor.
SmartPtrBase() : mPtr(NULL), mRefCount(NULL) {}
// Normal constructor. This takes ownership of |ptr|, though only
// template instances are capable of destroying the object.
explicit SmartPtrBase(void* ptr);
// Copy-constructor, this increments the reference count.
SmartPtrBase(const SmartPtrBase& other);
// Assignment operator, also increments the reference count.
SmartPtrBase& operator=(const SmartPtrBase& other);
// Nothing happens in this destructor, the real work must be performed
// in subclasses.
~SmartPtrBase() {}
// Used to enable 'if (smart_ptr) { ... }' properly.
operator void*() const {
return mPtr;
}
// Return internal reference count value, only use for unit testing.
int getRefCount() const;
protected:
// Used internally to increment the reference count.
void addRef();
// Copy the |other| into this instance, returns the old pointer value
// if it needs to be destroyed by the caller, or NULL otherwise.
void* copyFrom(const SmartPtrBase& other);
// Used internally to decrement the reference count, if it reaches 0,
// returns the pointer to be destroyed, NULL otherwise.
void* release();
void* mPtr;
RefCount* mRefCount;
};
// The real template class to be used for smart pointers.
// Typical uses:
//
// SmartPtr<Foo> ptr(new Foo()); // takes ownership.
// SmartPtr<Foo> ptr2; // empty pointer.
// ptr2 = ptr; // copies pointer + increment reference count.
// Foo* obj = ptr.Ptr(); // access pointed object.
// ptr->DoStuff(); // operate directly on pointed object.
// (*ptr)->DoStuff(); // same here.
//
// On scope exit, the internal reference count is decremented and the
// object is deleted automatically when it reaches 0, indicating that
// there are no more owners.
//
// IMPORTANT: You need to be sure that only one 'chain' of smart pointers
// own a given object. I.e. the following is incorrect:
//
// Foo* foo = new Foo(); // create new instance.
// SmartPtr<Foo> ptr(foo); // |ptr| takes ownership of |foo|.
// SmartPtr<Foo> ptr2(foo); // |ptr2| takes also ownership of |foo|.
//
// The problem is that |ptr| and |ptr2| don't know anything about each
// other, and will not share the same reference count. Once a smart pointer
// owns an object, only use other smart pointers that are copy-constructed
// or assigned with the initial one to keep everything consistent.
template <class T>
class SmartPtr : public emugl::SmartPtrBase {
public:
// Default constructor. The instance holds a NULL pointer.
SmartPtr() : SmartPtrBase() {}
// Regular constructor, takes ownership of |ptr|.
explicit SmartPtr(T* ptr) : SmartPtrBase(ptr) {}
// Copy-constructor, |this| and |other| will share the same internal
// reference count, which is incremented by 1.
SmartPtr(const SmartPtr& other)
: SmartPtrBase(reinterpret_cast<const SmartPtrBase&>(other)) {}
// Assignment operator, same semantics as copy-constructor.
SmartPtr& operator=(const SmartPtr& other) {
void* old_ptr = copyFrom(static_cast<const SmartPtrBase&>(other));
if (old_ptr)
delete reinterpret_cast<T*>(old_ptr);
return *this;
}
// Destructor, decrements reference count and destroys the object
// if it reaches 0 (indicating this was the last owning smart pointer).
~SmartPtr() {
void* ptr = release();
if (ptr)
delete reinterpret_cast<T*>(ptr);
}
// Return owned object instance, or NULL.
T* Ptr() const {
return reinterpret_cast<T*>(mPtr);
}
// Return owned object instance, or NULL
const T* constPtr() const {
return reinterpret_cast<const T*>(mPtr);
}
// Operate directly on owned object.
T* operator->() const {
return Ptr();
}
// Return reference to owned object.
T& operator*() const {
return *Ptr();
}
};
} // namespace emugl
#endif // EMUGL_SMART_PTR_H

View file

@ -0,0 +1,140 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/smart_ptr.h"
#include <gtest/gtest.h>
namespace emugl {
// This Test sub-class is used to track allocations and deallocations of
// the MyTestClass instances that are created through newInstance().
// See below for typical usage.
class SmartPtrTest : public testing::Test {
public:
SmartPtrTest() : mNewCount(0), mDeleteCount(0), mDoCount(0) {}
~SmartPtrTest() {
mNewCount = 0;
mDoCount = 0;
mDeleteCount = 0;
}
class MyClass;
MyClass* newInstance() {
return new MyClass(this);
}
class MyClass {
public:
MyClass(SmartPtrTest* test) : mTest(test) {
mTest->mNewCount++;
}
void doStuff() {
mTest->mDoCount++;
}
~MyClass() {
mTest->mDeleteCount++;
}
private:
SmartPtrTest* mTest;
};
int mNewCount;
int mDeleteCount;
int mDoCount;
};
TEST_F(SmartPtrTest, Empty) {
SmartPtr<MyClass> ptr;
EXPECT_FALSE(ptr.Ptr());
EXPECT_EQ(0, mNewCount);
EXPECT_EQ(0, mDeleteCount);
EXPECT_EQ(0, mDoCount);
}
TEST_F(SmartPtrTest, SingleRef) {
MyClass* obj = newInstance();
EXPECT_EQ(1, mNewCount);
{
SmartPtr<MyClass> ptr(obj);
EXPECT_EQ(obj, ptr.Ptr());
EXPECT_EQ(1, mNewCount);
EXPECT_EQ(0, mDeleteCount);
EXPECT_EQ(0, mDoCount);
}
// Check that the object was deleted.
EXPECT_EQ(1, mDeleteCount);
}
TEST_F(SmartPtrTest, CopyConstructor) {
MyClass* obj = newInstance();
EXPECT_EQ(1, mNewCount);
{
SmartPtr<MyClass> ptr1(obj);
{
SmartPtr<MyClass> ptr2(ptr1);
EXPECT_EQ(2, ptr1.getRefCount());
EXPECT_EQ(2, ptr2.getRefCount());
EXPECT_EQ(1, mNewCount);
EXPECT_EQ(0, mDeleteCount);
EXPECT_EQ(0, mDoCount);
}
EXPECT_EQ(1, mNewCount);
EXPECT_EQ(0, mDeleteCount);
EXPECT_EQ(0, mDoCount);
}
EXPECT_EQ(1, mNewCount);
EXPECT_EQ(1, mDeleteCount);
EXPECT_EQ(0, mDoCount);
}
TEST_F(SmartPtrTest, AssignmentOperator) {
SmartPtr<MyClass> ptr1(newInstance());
SmartPtr<MyClass> ptr2(newInstance());
EXPECT_EQ(2, mNewCount);
EXPECT_EQ(0, mDeleteCount);
EXPECT_EQ(0, mDoCount);
ptr2 = ptr1;
EXPECT_EQ(2, mNewCount);
EXPECT_EQ(1, mDeleteCount);
EXPECT_EQ(ptr1.Ptr(), ptr2.Ptr());
EXPECT_EQ(2, ptr1.getRefCount());
EXPECT_EQ(2, ptr2.getRefCount());
}
TEST_F(SmartPtrTest, ArrowOperator) {
SmartPtr<MyClass> ptr(newInstance());
ptr->doStuff();
EXPECT_EQ(1, mDoCount);
(*ptr).doStuff();
EXPECT_EQ(2, mDoCount);
}
} // namespace emugl

View file

@ -0,0 +1,247 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/sockets.h"
#include <errno.h>
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdio.h>
#endif
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
namespace emugl {
namespace {
static void socketSetDontLinger(int s) {
#ifdef _WIN32
// TODO: Verify default behavior on WINDOWS
#else
// Ungraceful shutdown, no reason to linger at all
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
if(setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger) < 0)
perror("Setting socket option SO_LINGER={on, 0} failed");
#endif
}
static void socketSetReuseAddress(int s) {
#ifdef _WIN32
// The default behaviour on Windows is equivalent to SO_REUSEADDR
// so we don't need to set this option. Moreover, one should never
// set this option with WinSock because it's badly implemented and
// generates a huge security issue. See:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx
#else
int val = 1;
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
perror("Setting socket option SO_REUSEADDR failed");
#endif
}
// Helper union to store a socket address.
struct SockAddr {
socklen_t len;
union {
sockaddr generic;
sockaddr_in inet;
#ifndef _WIN32
sockaddr_un local;
#endif
};
int getFamily() const { return generic.sa_family; }
void initEmpty() {
::memset(this, 0, sizeof(*this));
this->len = static_cast<socklen_t>(sizeof(*this));
}
int initFromInet(uint32_t ip_address, int port) {
if (port < 0 || port >= 65536)
return -EINVAL;
::memset(this, 0, sizeof(*this));
this->inet.sin_family = AF_INET;
this->inet.sin_port = htons(port);
this->inet.sin_addr.s_addr = htonl(ip_address);
this->len = sizeof(this->inet);
return 0;
}
int initFromLocalhost(int port) {
return initFromInet(0x7f000001, port);
}
#ifndef _WIN32
// Initialize the SockAddr from a Unix path. Returns 0 on success,
// or -errno code on failure.
int initFromUnixPath(const char* path) {
if (!path || !path[0])
return -EINVAL;
size_t pathLen = ::strlen(path);
if (pathLen >= sizeof(local.sun_path))
return -E2BIG;
::memset(this, 0, sizeof(*this));
this->local.sun_family = AF_LOCAL;
::memcpy(this->local.sun_path, path, pathLen + 1U);
this->len = pathLen + offsetof(sockaddr_un, sun_path);
return 0;
}
#endif
};
int socketBindInternal(const SockAddr* addr, int socketType) {
int s = ::socket(addr->getFamily(), socketType, 0);
if (s < 0) {
perror("Could not create socket to bind");
return -errno;
}
socketSetDontLinger(s);
socketSetReuseAddress(s);
// Bind to the socket.
if (::bind(s, &addr->generic, addr->len) < 0 ||
::listen(s, 5) < 0) {
int ret = -errno;
perror("Could not bind or listen to socket");
::close(s);
return ret;
}
return s;
}
int socketConnectInternal(const SockAddr* addr, int socketType) {
int s = ::socket(addr->getFamily(), socketType, 0);
if (s < 0) {
perror("Could not create socket to connect");
return -errno;
}
socketSetDontLinger(s);
socketSetReuseAddress(s);
int ret;
do {
ret = ::connect(s, &addr->generic, addr->len);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
ret = -errno;
::close(s);
return ret;
}
return s;
}
} // namespace
void socketTcpDisableNagle(int s) {
// disable Nagle algorithm to improve bandwidth of small
// packets which are quite common in our implementation.
#ifdef _WIN32
DWORD flag;
#else
int flag;
#endif
flag = 1;
setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
(const char*)&flag, sizeof(flag));
}
int socketGetPort(int s) {
SockAddr addr;
addr.initEmpty();
if (getsockname(s, &addr.generic, &addr.len) < 0) {
return -errno;
}
switch (addr.generic.sa_family) {
case AF_INET:
return ntohs(addr.inet.sin_port);
default:
;
}
return -EINVAL;
}
#ifndef _WIN32
int socketLocalServer(const char* path, int socketType) {
SockAddr addr;
int ret = addr.initFromUnixPath(path);
if (ret < 0) {
return ret;
}
return socketBindInternal(&addr, socketType);
}
int socketLocalClient(const char* path, int socketType) {
SockAddr addr;
int ret = addr.initFromUnixPath(path);
if (ret < 0) {
return ret;
}
return socketConnectInternal(&addr, socketType);
}
#endif // !_WIN32
int socketTcpLoopbackServer(int port, int socketType) {
SockAddr addr;
int ret = addr.initFromLocalhost(port);
if (ret < 0) {
return ret;
}
return socketBindInternal(&addr, socketType);
}
int socketTcpLoopbackClient(int port, int socketType) {
SockAddr addr;
int ret = addr.initFromLocalhost(port);
if (ret < 0) {
return ret;
}
return socketConnectInternal(&addr, socketType);
}
int socketTcpClient(const char* hostname, int port, int socketType) {
// TODO(digit): Implement this.
return -ENOSYS;
}
int socketAccept(int serverSocket) {
int ret;
do {
ret = ::accept(serverSocket, NULL, NULL);
} while (ret < 0 && errno == EINTR);
return ret;
}
} // namespace emugl

View file

@ -0,0 +1,57 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_SOCKETS_H
#define EMUGL_COMMON_SOCKETS_H
// A set of helper functions used to deal with sockets in a portable way.
namespace emugl {
// Disable Nagle's algorithm for socket descriptor |s|. This assumes
// that |s| is a TCP socket descriptor.
void socketTcpDisableNagle(int s);
// Return the port associated with a given IP or IPv6 socket,
// or -errno code on failure.
int socketGetPort(int s);
// Bind to a local/Unix path, and return its socket descriptor on success,
// or -errno code on failure.
int socketLocalServer(const char* path, int socketType);
// Connect to a Unix local path, and return a new socket descriptor
// on success, or -errno code on failure.
int socketLocalClient(const char* path, int socketType);
// Bind to a localhost TCP socket, and return its socket descriptor on
// success, or -errno code on failure.
int socketTcpLoopbackServer(int port, int socketType);
// Connect to a localhost TCP port, and return a new socket descriptor on
// success, or -errno code on failure.
int socketTcpLoopbackClient(int port, int socketType);
// Connect to a TCP host, and return a new socket descriptor on
// success, or -errno code on failure.
int socketTcpClient(const char* hostname, int port, int socketType);
// Accept a new connection. |serverSocket| must be a bound server socket
// descriptor. Returns new socket descriptor on success, or -errno code
// on failure.
int socketAccept(int serverSocket);
} // namespace emugl
#endif // EMUGL_COMMON_SOCKETS_H

View file

@ -0,0 +1,22 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This source file must be compiled into a simple shared library which
// will be used by shared_library_unittest.cpp to verify that the
// emugl::SharedLibrary class works properly.
extern "C" int foo_function(void) {
return 42;
}

View file

@ -0,0 +1,78 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_TESTING_TEST_THREAD_H
#define EMUGL_COMMON_TESTING_TEST_THREAD_H
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# include <windows.h>
#else
# include <pthread.h>
#endif
namespace emugl {
// Very basic platform thread wrapper that only uses a tiny stack.
// This shall only be used during unit testing, and allows test code
// to not depend on the implementation of emugl::Thread.
class TestThread {
public:
// Main thread function type.
typedef void* (ThreadFunction)(void* param);
// Constructor actually launches a new platform thread.
TestThread(ThreadFunction* func, void* funcParam) {
#ifdef _WIN32
mThread = CreateThread(NULL,
16384,
(DWORD WINAPI (*)(void*))func,
funcParam,
0,
NULL);
#else
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, 16384);
pthread_create(&mThread, &attr, func, funcParam);
pthread_attr_destroy(&attr);
#endif
}
~TestThread() {
#ifdef _WIN32
CloseHandle(mThread);
#endif
}
void join() {
#ifdef _WIN32
WaitForSingleObject(mThread, INFINITE);
#else
void* ret = NULL;
pthread_join(mThread, &ret);
#endif
}
private:
#ifdef _WIN32
HANDLE mThread;
#else
pthread_t mThread;
#endif
};
} // namespace emugl
#endif // EMUGL_COMMON_TESTING_TEST_THREAD_H

View file

@ -0,0 +1,103 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_THREAD_H
#define EMUGL_COMMON_THREAD_H
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif
#include <stdint.h>
namespace emugl {
// Wrapper class for platform-specific threads.
// To create your own thread, define a sub-class of emugl::Thread
// and override its main() method.
//
// For example:
//
// class MyThread : public emugl::Thread {
// public:
// MyThread() : Thread() {}
//
// virtual intptr_t main() {
// ... main thread loop implementation
// return 0;
// }
// };
//
// ...
//
// // Create new instance, but does not start it.
// MyThread* thread = new MyThread();
//
// // Start the thread.
// thread->start();
//
// // Wait for thread completion, and gets result into |exitStatus|.
// int exitStatus;
// thread->wait(&exitStatus);
//
class Thread {
public:
// Public constructor.
Thread();
// Virtual destructor.
virtual ~Thread();
// Override this method in your own thread sub-classes. This will
// be called when start() is invoked on the Thread instance.
virtual intptr_t main() = 0;
// Start a thread instance. Return true on success, false otherwise
// (e.g. if the thread was already started or terminated).
bool start();
// Wait for thread termination and retrieve exist status into
// |*exitStatus|. Return true on success, false otherwise.
// NOTE: |exitStatus| can be NULL.
bool wait(intptr_t *exitStatus);
// Check whether a thread has terminated. On success, return true
// and sets |*exitStatus|. On failure, return false.
// NOTE: |exitStatus| can be NULL.
bool tryWait(intptr_t *exitStatus);
private:
#ifdef _WIN32
static DWORD WINAPI thread_main(void* arg);
HANDLE mThread;
DWORD mThreadId;
CRITICAL_SECTION mLock;
#else // !WIN32
static void* thread_main(void* arg);
pthread_t mThread;
pthread_mutex_t mLock;
bool mJoined;
#endif
intptr_t mExitStatus;
bool mIsRunning;
};
} // namespace emugl
#endif // EMUGL_COMMON_THREAD_H

View file

@ -0,0 +1,136 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/thread.h"
#include "emugl/common/thread_store.h"
#include <assert.h>
#include <stdio.h>
namespace emugl {
namespace {
class ScopedLocker {
public:
ScopedLocker(pthread_mutex_t* mutex) : mMutex(mutex) {
pthread_mutex_lock(mMutex);
}
~ScopedLocker() {
pthread_mutex_unlock(mMutex);
}
private:
pthread_mutex_t* mMutex;
};
} // namespace
Thread::Thread() :
mThread((pthread_t)NULL),
mLock(),
mJoined(false),
mExitStatus(0),
mIsRunning(false) {
pthread_mutex_init(&mLock, NULL);
}
Thread::~Thread() {
assert(!mIsRunning);
assert(mJoined);
pthread_mutex_destroy(&mLock);
}
bool Thread::start() {
bool ret = true;
pthread_mutex_lock(&mLock);
mIsRunning = true;
if (pthread_create(&mThread, NULL, thread_main, this)) {
ret = false;
mIsRunning = false;
}
pthread_mutex_unlock(&mLock);
return ret;
}
bool Thread::wait(intptr_t *exitStatus) {
{
ScopedLocker locker(&mLock);
if (!mIsRunning) {
// Thread already stopped.
if (exitStatus) {
*exitStatus = mExitStatus;
}
if (!mJoined) {
// reclaim thread stack
pthread_join(mThread, NULL);
mJoined = true;
}
return true;
}
}
// NOTE: Do not hold the lock when waiting for the thread to ensure
// it can update mIsRunning and mExitStatus properly in thread_main
// without blocking.
void *retval;
if (pthread_join(mThread, &retval)) {
return false;
}
if (exitStatus) {
*exitStatus = (intptr_t)retval;
}
// Note: Updating mJoined must be performed inside the lock to avoid
// race conditions between two threads waiting for the same thread
// that just completed its execution.
{
ScopedLocker locker(&mLock);
mJoined = true;
}
return true;
}
bool Thread::tryWait(intptr_t *exitStatus) {
ScopedLocker locker(&mLock);
if (mIsRunning) {
return false;
}
if (!mJoined) {
// Reclaim stack.
pthread_join(mThread, NULL);
mJoined = true;
}
if (exitStatus) {
*exitStatus = mExitStatus;
}
return true;
}
// static
void* Thread::thread_main(void *arg) {
Thread* self = reinterpret_cast<Thread*>(arg);
intptr_t ret = self->main();
pthread_mutex_lock(&self->mLock);
self->mIsRunning = false;
self->mExitStatus = ret;
pthread_mutex_unlock(&self->mLock);
::emugl::ThreadStore::OnThreadExit();
return (void*)ret;
}
} // namespace emugl

View file

@ -0,0 +1,242 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/thread_store.h"
#ifdef _WIN32
#include "emugl/common/lazy_instance.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Set to 1 to print debug messages.
#define DEBUG_THREAD_STORE 0
#if DEBUG_THREAD_STORE
# define D(...) do { printf("%s:%d: ", __FUNCTION__, __LINE__); printf(__VA_ARGS__); fflush(stdout); } while (0)
#else
# define D(...) ((void)0)
#endif
namespace emugl {
#ifdef _WIN32
namespace {
// The ThreadStore implementation on Windows is very tricky, because
// TlsAlloc() doesn't allow one to provide a destructor function. As
// such threads are expected to destroy all TLS values explicitely.
//
// To solve this issue, this source file provides a static method called
// ThreadStore::OnThreadExit() that must be called when a thread exits,
// which will cleanup all values for the current thread.
//
// But this forces us to track thread-specific values ourselves.
// Maximum amount of thread-specific slots supported by this implementation.
enum {
kMaxTlsSlots = 64
};
// TlsSlotArray is a thread-specific array of values. Instances will
// be stored in a Win32 TLS value controlled by a single master TLS
// key.
//
typedef void* TlsSlotArray[kMaxTlsSlots];
// Global state shared by all threads
class GlobalState {
public:
GlobalState() {
D("Entering\n");
mMasterTls = TlsAlloc();
D("Master TLS = %d\n", (int)mMasterTls);
InitializeCriticalSection(&mSection);
mLastIndex = 0;
::memset(mDestructors, 0, sizeof(mDestructors));
D("Exiting\n");
}
// Register a new TLS key, or return -1 on error (too many keys).
// |destroy| is the destructor function for the key.
int registerKey(ThreadStore::Destructor* destroy) {
D("Entering destroy=%p\n", destroy);
int ret = -1;
EnterCriticalSection(&mSection);
if (mLastIndex < kMaxTlsSlots) {
ret = mLastIndex++;
mDestructors[ret] = destroy;
}
LeaveCriticalSection(&mSection);
D("Exiting newKey=%d\n", ret);
return ret;
}
void unregisterKey(int key) {
D("key=%d\n", key);
if (key < 0 || key >= kMaxTlsSlots) {
D("Invalid key\n");
return;
}
// Note: keys are not reusable, but remove the destructor to avoid
// crashes in leaveCurrentThread() when it points to a function that
// is going to be unloaded from the process' address space.
EnterCriticalSection(&mSection);
mDestructors[key] = NULL;
LeaveCriticalSection(&mSection);
D("Exiting\n");
}
// Get the current thread-local value for a given |key|.
void* getValue(int key) const {
D("Entering key=%d\n", key);
if (key < 0 || key >= kMaxTlsSlots) {
D("Invalid key, result=NULL\n");
return NULL;
}
TlsSlotArray* array = getArray();
void* ret = (*array)[key];
D("Exiting keyValue=%p\n", ret);
return ret;
}
// Set the current thread-local |value| for a given |key|.
void setValue(int key, void* value) {
D("Entering key=%d\n",key);
if (key < 0 || key >= kMaxTlsSlots) {
D("Invalid key, returning\n");
return;
}
TlsSlotArray* array = getArray();
(*array)[key] = value;
D("Exiting\n");
}
// Call this when a thread exits to destroy all its thread-local values.
void leaveCurrentThread() {
D("Entering\n");
TlsSlotArray* array =
reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls));
if (!array) {
D("Exiting, no thread-local data in this thread\n");
return;
}
for (size_t n = 0; n < kMaxTlsSlots; ++n) {
void* value = array[n];
if (value) {
(*array)[n] = NULL;
// NOTE: In theory, a destructor could reset the slot to
// a new value, and we would have to loop in this function
// in interesting ways. In practice, ignore the issue.
EnterCriticalSection(&mSection);
ThreadStore::Destructor* destroy = mDestructors[n];
LeaveCriticalSection(&mSection);
if (destroy) {
D("Calling destructor %p for key=%d, with value=%p\n",
destroy, (int)n, value);
(*destroy)(value);
}
}
}
TlsSetValue(mMasterTls, NULL);
::free(array);
D("Exiting\n");
}
private:
// Return the thread-local array of TLS slots for the current thread.
// Cannot return NULL.
TlsSlotArray* getArray() const {
D("Entering\n");
TlsSlotArray* array =
reinterpret_cast<TlsSlotArray*>(TlsGetValue(mMasterTls));
if (!array) {
array = reinterpret_cast<TlsSlotArray*>(
::calloc(sizeof(*array), 1));
TlsSetValue(mMasterTls, array);
D("Allocated new array at %p\n", array);
} else {
D("Retrieved array at %p\n", array);
}
return array;
}
DWORD mMasterTls;
CRITICAL_SECTION mSection;
int mLastIndex;
ThreadStore::Destructor* mDestructors[kMaxTlsSlots];
};
LazyInstance<GlobalState> gGlobalState = LAZY_INSTANCE_INIT;
} // namespace
ThreadStore::ThreadStore(Destructor* destroy) {
D("Entering this=%p destroy=%p\n", this, destroy);
mKey = gGlobalState->registerKey(destroy);
D("Exiting this=%p key=%d\n", this, mKey);
}
ThreadStore::~ThreadStore() {
D("Entering this=%p\n", this);
GlobalState* state = gGlobalState.ptr();
state->unregisterKey(mKey);
D("Exiting this=%p\n", this);
}
void* ThreadStore::get() const {
D("Entering this=%p\n", this);
void* ret = gGlobalState->getValue(mKey);
D("Exiting this=%p value=%p\n", this, ret);
return ret;
}
void ThreadStore::set(void* value) {
D("Entering this=%p value=%p\n", this, value);
gGlobalState->setValue(mKey, value);
D("Exiting this=%p\n", this);
}
// static
void ThreadStore::OnThreadExit() {
gGlobalState->leaveCurrentThread();
}
#else // !_WIN32
ThreadStore::ThreadStore(Destructor* destroy) {
int ret = pthread_key_create(&mKey, destroy);
if (ret != 0) {
fprintf(stderr,
"Could not create thread store key: %s\n",
strerror(ret));
exit(1);
}
}
ThreadStore::~ThreadStore() {
pthread_key_delete(mKey);
}
#endif // !_WIN32
} // namespace emugl

View file

@ -0,0 +1,110 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_THREAD_STORE_H
#define EMUGL_COMMON_THREAD_STORE_H
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN 1
# include <windows.h>
#else
# include <pthread.h>
#endif
namespace emugl {
// A class to model storage of thread-specific values, that can be
// destroyed on thread exit.
//
// Note that on Windows, a thread must call OnThreadExit() explicitly
// here to ensure that the values are probably discarded. This is an
// unfortunate requirement of the Win32 API, which doesn't support
// destructors at all.
//
// There are various hacks on the web to try to achieve this automatically
// (e.g. [1]) but they rely on using the Microsoft build tools,
// which doesn't work for us.
//
// Note another important issue with ThreadStore instances: if you create
// one instance in a shared library, you need to make sure that it is
// always destroyed before the library is unloaded. Otherwise, future
// thread exit will likely crash, due to calling a destructor function
// that is no longer in the process' address space.
//
// Finally, destroying an instance does _not_ free the corresponding values,
// because doing so properly requires coordinating all participating threads,
// which is impossible to achieve in the most general case. Thus, consider
// that thread-local values are always leaked on library unload, or on
// program exit.
//
// [1] http://stackoverflow.com/questions/14538159/about-tls-callback-in-windows
class ThreadStore {
public:
// Type of a function used to destroy a thread-specific value that
// was previously assigned by calling set().
typedef void (Destructor)(void* value);
// Initialize instance so that is hold keys that must be destroyed
// on thread exit by calling |destroy|.
explicit ThreadStore(Destructor* destroy);
// NOTE: Destructor don't free the thread-local values, but are required
// to avoid crashes (see note above).
~ThreadStore();
// Retrieve current thread-specific value from store.
#ifdef _WIN32
void* get() const;
#else
inline void* get() const {
return pthread_getspecific(mKey);
}
#endif
// Set the new thread-specific value.
#ifdef _WIN32
void set(void* value);
#else
inline void set(void* value) {
pthread_setspecific(mKey, value);
}
#endif
#ifdef _WIN32
// Each thread should call this function on exit to ensure that
// all corresponding TLS values are properly freed.
static void OnThreadExit();
#else
// Nothing to do on Posix.
static inline void OnThreadExit() {}
#endif
private:
// Ensure you can't create an empty ThreadStore instance, or simply
// copy it in any way.
ThreadStore();
ThreadStore(const ThreadStore&);
ThreadStore& operator=(const ThreadStore&);
#ifdef _WIN32
int mKey;
#else
pthread_key_t mKey;
#endif
};
} // namespace emugl
#endif // EMUGL_COMMON_THREAD_STORE_H

View file

@ -0,0 +1,146 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/thread_store.h"
#include "emugl/common/mutex.h"
#include "emugl/common/testing/test_thread.h"
#include <gtest/gtest.h>
namespace emugl {
namespace {
// Helper class used to count instance creation and destruction.
class StaticCounter {
public:
enum {
kMaxInstances = 1000,
};
StaticCounter() {
Mutex::AutoLock lock(mMutex);
if (mCreationCount < kMaxInstances)
mInstances[mCreationCount] = this;
mCreationCount++;
}
~StaticCounter() {
Mutex::AutoLock lock(mMutex);
mDestructionCount++;
}
static void reset() {
Mutex::AutoLock lock(mMutex);
mCreationCount = 0;
mDestructionCount = 0;
}
static size_t getCreationCount() {
Mutex::AutoLock lock(mMutex);
return mCreationCount;
}
static size_t getDestructionCount() {
Mutex::AutoLock lock(mMutex);
return mDestructionCount;
}
static void freeAll() {
for (size_t n = 0; n < kMaxInstances; ++n)
delete mInstances[n];
}
private:
static Mutex mMutex;
static size_t mCreationCount;
static size_t mDestructionCount;
static StaticCounter* mInstances[kMaxInstances];
};
Mutex StaticCounter::mMutex;
size_t StaticCounter::mCreationCount = 0;
size_t StaticCounter::mDestructionCount = 0;
StaticCounter* StaticCounter::mInstances[kMaxInstances];
} // namespace
// Just check that we can create a new ThreadStore with an empty
// destructor, and use it in the current thread.
TEST(ThreadStore, MainThreadWithoutDestructor) {
ThreadStore store(NULL);
static int x = 42;
store.set(&x);
EXPECT_EQ(&x, store.get());
}
// The following test checks that exiting a thread correctly deletes
// any thread-local value stored in it.
static void simplyDestroy(void* value) {
delete (StaticCounter*) value;
}
static void* simpleThreadFunc(void* param) {
ThreadStore* store = static_cast<ThreadStore*>(param);
store->set(new StaticCounter());
ThreadStore::OnThreadExit();
return NULL;
}
TEST(ThreadStore, ThreadsWithDestructor) {
ThreadStore store(simplyDestroy);
const size_t kNumThreads = 1000;
TestThread* threads[kNumThreads];
StaticCounter::reset();
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n] = new TestThread(&simpleThreadFunc, &store);
}
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n]->join();
}
EXPECT_EQ(kNumThreads, StaticCounter::getCreationCount());
EXPECT_EQ(kNumThreads, StaticCounter::getDestructionCount());
for (size_t n = 0; n < kNumThreads; ++n) {
delete threads[n];
}
}
TEST(ThreadStore, ThreadsWithoutDestructor) {
ThreadStore store(NULL);
const size_t kNumThreads = 1000;
TestThread* threads[kNumThreads];
StaticCounter::reset();
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n] = new TestThread(&simpleThreadFunc, &store);
}
for (size_t n = 0; n < kNumThreads; ++n) {
threads[n]->join();
}
EXPECT_EQ(kNumThreads, StaticCounter::getCreationCount());
EXPECT_EQ(0U, StaticCounter::getDestructionCount());
StaticCounter::freeAll();
for (size_t n = 0; n < kNumThreads; ++n) {
delete threads[n];
}
}
} // namespace emugl

View file

@ -0,0 +1,164 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/thread.h"
#include "emugl/common/mutex.h"
#include <gtest/gtest.h>
namespace emugl {
namespace {
// A simple thread instance that does nothing at all and exits immediately.
class EmptyThread : public ::emugl::Thread {
public:
intptr_t main() { return 42; }
};
class CountingThread : public ::emugl::Thread {
public:
class State {
public:
State() : mLock(), mCount(0) {}
~State() {}
void increment() {
mLock.lock();
mCount++;
mLock.unlock();
}
int count() const {
int ret;
mLock.lock();
ret = mCount;
mLock.unlock();
return ret;
}
private:
mutable Mutex mLock;
int mCount;
};
CountingThread(State* state) : mState(state) {}
intptr_t main() {
mState->increment();
return 0;
}
private:
State* mState;
};
class WaitingThread : public ::emugl::Thread {
public:
WaitingThread(Mutex* lock) : mLock(lock) {}
intptr_t main() {
// Try to acquire lock.
mLock->lock();
// Then try to release it.
mLock->unlock();
return 0;
}
private:
Mutex* mLock;
};
} // namespace
TEST(ThreadTest, WaitForSimpleThread) {
Thread* thread = new EmptyThread();
EXPECT_TRUE(thread);
EXPECT_TRUE(thread->start());
intptr_t status;
EXPECT_TRUE(thread->wait(&status));
EXPECT_EQ(42, status);
}
TEST(ThreadTest, WaitForMultipleThreads) {
CountingThread::State state;
const size_t kMaxThreads = 100;
Thread* threads[kMaxThreads];
// Create all threads.
for (size_t n = 0; n < kMaxThreads; ++n) {
threads[n] = new CountingThread(&state);
EXPECT_TRUE(threads[n]) << "thread " << n;
}
// Start them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_TRUE(threads[n]->start()) << "thread " << n;
}
// Wait for them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_TRUE(threads[n]->wait(NULL)) << "thread " << n;
}
// Check state.
EXPECT_EQ((int)kMaxThreads, state.count());
// Delete them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
delete threads[n];
}
}
TEST(ThreadTest, TryWaitForMultipleThreads) {
Mutex lock;
const size_t kMaxThreads = 100;
Thread* threads[kMaxThreads];
// Create all threads.
for (size_t n = 0; n < kMaxThreads; ++n) {
threads[n] = new WaitingThread(&lock);
EXPECT_TRUE(threads[n]) << "thread " << n;
}
// Acquire the lock, this will block all threads.
lock.lock();
// Start them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_TRUE(threads[n]->start()) << "thread " << n;
}
// Check that tryWait() fails for all threads.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_FALSE(threads[n]->tryWait(NULL)) << "thread" << n;
}
// Release the lock, this will unblock all threads.
lock.unlock();
// Wait for them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
EXPECT_TRUE(threads[n]->wait(NULL)) << "thread " << n;
EXPECT_TRUE(threads[n]->tryWait(NULL)) << "thread " << n;
}
// Delete them all.
for (size_t n = 0; n < kMaxThreads; ++n) {
delete threads[n];
}
}
} // namespace emugl

View file

@ -0,0 +1,121 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/thread.h"
#include "emugl/common/thread_store.h"
namespace emugl {
namespace {
class ScopedLocker {
public:
ScopedLocker(CRITICAL_SECTION* section) : mSection(section) {
EnterCriticalSection(mSection);
}
~ScopedLocker() {
LeaveCriticalSection(mSection);
}
private:
CRITICAL_SECTION* mSection;
};
} // namespace
Thread::Thread() :
mThread(INVALID_HANDLE_VALUE),
mThreadId(0),
mExitStatus(0),
mIsRunning(false) {
InitializeCriticalSection(&mLock);
}
Thread::~Thread() {
if(mThread != INVALID_HANDLE_VALUE) {
CloseHandle(mThread);
}
DeleteCriticalSection(&mLock);
}
bool Thread::start() {
ScopedLocker locker(&mLock);
bool ret = true;
mIsRunning = true;
mThread = CreateThread(NULL, 0, &Thread::thread_main, this, 0, &mThreadId);
if (!mThread) {
ret = false;
mIsRunning = false;
}
return ret;
}
bool Thread::wait(intptr_t* exitStatus) {
{
ScopedLocker locker(&mLock);
if (!mIsRunning) {
// Thread already stopped.
if (exitStatus) {
*exitStatus = mExitStatus;
}
return true;
}
}
// NOTE: Do not hold lock during wait to aloow thread_main to
// properly update mIsRunning and mExitStatus on thread exit.
if (WaitForSingleObject(mThread, INFINITE) == WAIT_FAILED) {
return false;
}
if (exitStatus) {
ScopedLocker locker(&mLock);
*exitStatus = mExitStatus;
}
return true;
}
bool Thread::tryWait(intptr_t* exitStatus) {
ScopedLocker locker(&mLock);
if (mIsRunning && WaitForSingleObject(mThread, 0) != WAIT_OBJECT_0) {
return false;
}
if (exitStatus) {
*exitStatus = mExitStatus;
}
return true;
}
// static
DWORD WINAPI Thread::thread_main(void *arg)
{
Thread* self = reinterpret_cast<Thread*>(arg);
intptr_t ret = self->main();
EnterCriticalSection(&self->mLock);
self->mIsRunning = false;
self->mExitStatus = ret;
LeaveCriticalSection(&self->mLock);
// Ensure all thread-local values are released for this thread.
::emugl::ThreadStore::OnThreadExit();
return static_cast<DWORD>(ret);
}
} // namespace emugl

View file

@ -0,0 +1,225 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef EMUGL_COMMON_UNIQUE_INTEGER_MAP_H
#define EMUGL_COMMON_UNIQUE_INTEGER_MAP_H
#include "emugl/common/pod_vector.h"
#include <stdint.h>
namespace emugl {
// Helper template class that implements a bi-directional mapping between
// two integer types |A| and |B|. More specifically:
//
// - The map allocates values of type |B| when a key of type |A| is entered
// in the map.
//
// - keys and values cannot be 0, which is reserved (i.e. means 'invalid').
//
// This is used in EmuGL to map liberal 'void*' values (e.g. EGLimages ones)
// to unique 32-bit IDs that can be written to / read from the wire protocol.
template <typename A, typename B>
class UniqueIntegerMap {
public:
UniqueIntegerMap() : mForwardPairs(), mBackwardPairs() {}
~UniqueIntegerMap() {}
// Return true iff the map is empty.
const bool empty() const { return mForwardPairs.empty(); }
// Return the number of (key,value) pairs in the map.
size_t size() const { return mForwardPairs.size(); }
// Find the value associated with |key| in the map.
// Returns 0 in case of failure, or if |key| is 0.
B find(const A key) const;
// Find the key associated with a given |value| in the map.
// Returns 0 if |value| is 0, or in case of failure.
A findKeyFor(const B value) const;
// Add |key| to the map and return an automatically-allocated
// unique value for it. Return 0 if |key| is 0.
B add(const A key);
// Delete the entry associated with a given |key|. The
// corresponding value may be recycled by future calls to add().
void del(const A key);
private:
typedef struct {
A first;
B second;
} ForwardPair;
typedef struct {
B first;
A second;
} BackwardPair;
size_t findKeyIndexPlusOne(const A key) const;
size_t findValueIndexPlusOne(const B value) const;
B allocValue();
void freeValue(B value);
PodVector<ForwardPair> mForwardPairs;
PodVector<BackwardPair> mBackwardPairs;
B mLastValue;
PodVector<B> mFreeValues;
};
template <typename A, typename B>
B UniqueIntegerMap<A,B>::find(const A key) const {
size_t keyIndex = findKeyIndexPlusOne(key);
if (!keyIndex) {
return 0;
}
return mForwardPairs[keyIndex - 1U].second;
}
template <typename A, typename B>
A UniqueIntegerMap<A,B>::findKeyFor(const B value) const {
size_t valueIndex = findValueIndexPlusOne(value);
if (!valueIndex) {
return 0;
}
return mBackwardPairs[valueIndex - 1U].second;
}
template <typename A, typename B>
B UniqueIntegerMap<A,B>::add(const A key) {
// Binary search to find the proper insertion point for the key.
// Also checks that the key isn't already in the set.
size_t min = 0;
size_t max = mForwardPairs.size();
while (min < max) {
size_t mid = min + ((max - min) >> 1);
A midKey = mForwardPairs[mid].first;
if (midKey < key) {
min = mid + 1U;
} else if (midKey > key) {
max = mid;
} else {
// Already in the set.
return 0;
}
}
// Generate new unique value
B value = allocValue();
ForwardPair* pair = mForwardPairs.emplace(min);
pair->first = key;
pair->second = value;
// Binary search to find proper insertion point for the value.
min = 0;
max = mBackwardPairs.size();
while (min < max) {
size_t mid = min + ((max - min) >> 1);
B midValue = mBackwardPairs[mid].first;
if (midValue < value) {
min = mid + 1U;
} else {
max = mid;
}
}
BackwardPair* backPair = mBackwardPairs.emplace(min);
backPair->first = value;
backPair->second = key;
return value;
}
template <typename A, typename B>
void UniqueIntegerMap<A,B>::del(const A key) {
size_t keyIndex = findKeyIndexPlusOne(key);
if (!keyIndex) {
return;
}
B value = mForwardPairs[keyIndex - 1U].second;
size_t valueIndex = findValueIndexPlusOne(value);
mForwardPairs.remove(keyIndex - 1U);
mBackwardPairs.remove(valueIndex - 1U);
freeValue(value);
}
template <typename A, typename B>
size_t UniqueIntegerMap<A,B>::findKeyIndexPlusOne(const A key) const {
// Binary search in forward pair array.
size_t min = 0;
size_t max = mForwardPairs.size();
while (min < max) {
size_t mid = min + ((max - min) >> 1);
A midKey = mForwardPairs[mid].first;
if (midKey < key) {
min = mid + 1U;
} else if (midKey > key) {
max = mid;
} else {
return mid + 1U;
}
}
return 0U;
}
template <typename A, typename B>
size_t UniqueIntegerMap<A,B>::findValueIndexPlusOne(const B value) const {
// Binary search in revere pair array.
size_t min = 0;
size_t max = mBackwardPairs.size();
while (min < max) {
size_t mid = min + ((max - min) >> 1);
B midValue = mBackwardPairs[mid].first;
if (midValue < value) {
min = mid + 1U;
} else if (midValue > value) {
max = mid;
} else {
return mid + 1U;
}
}
return 0U;
}
template <typename A, typename B>
B UniqueIntegerMap<A,B>::allocValue() {
if (!mFreeValues.empty()) {
B result = mFreeValues[0];
mFreeValues.pop();
return result;
}
return ++mLastValue;
}
template <typename A, typename B>
void UniqueIntegerMap<A,B>::freeValue(B value) {
if (!value) {
return;
}
if (value == mLastValue) {
mLastValue--;
return;
}
mFreeValues.append(value);
}
} // namespace emugl
#endif // EMUGL_COMMON_INTEGER_MAP_H

View file

@ -0,0 +1,103 @@
// Copyright (C) 2014 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "emugl/common/unique_integer_map.h"
#include <gtest/gtest.h>
#include <stdio.h>
namespace emugl {
typedef UniqueIntegerMap<uintptr_t,uint32_t> MyMap;
TEST(UniqueIntegerMap, Empty) {
MyMap map;
EXPECT_TRUE(map.empty());
EXPECT_EQ(0U, map.size());
EXPECT_EQ(0U, map.find(0U));
EXPECT_EQ(0U, map.find(1U));
EXPECT_EQ(0U, map.find(2U));
EXPECT_EQ(0U, map.find(4U));
}
TEST(UniqueIntegerMap, AddOne) {
MyMap map;
uintptr_t key1 = 1U;
uint32_t val1 = map.add(key1);
EXPECT_NE(0U, val1);
EXPECT_EQ(val1, map.find(key1));
EXPECT_EQ(key1, map.findKeyFor(val1));
EXPECT_FALSE(map.empty());
EXPECT_EQ(1U, map.size());
EXPECT_EQ(0U, map.find(0));
EXPECT_EQ(0U, map.findKeyFor(0));
EXPECT_EQ(0U, map.find(key1 + 1));
EXPECT_EQ(0U, map.findKeyFor(val1 + 1));
}
TEST(UniqueIntegerMap, AddMultiple) {
MyMap map;
const size_t kCount = 100;
const size_t kKeyMultiplier = 3U; // must be >= 2.
uint32_t values[kCount];
for (size_t n = 0; n < kCount; ++n) {
uintptr_t key = 1U + n * kKeyMultiplier;
values[n] = map.add(key);
EXPECT_NE(0U, values[n]) << "key #" << n;
}
EXPECT_EQ(kCount, map.size());
for (size_t n = 0; n < kCount; ++n) {
uintptr_t key = 1U + n * kKeyMultiplier;
EXPECT_EQ(values[n], map.find(key)) << "key #" << n;
EXPECT_EQ(0U, map.find(key + 1U)) << "key #" << n;
}
for (size_t n = 0; n < kCount; ++n) {
uintptr_t key = 1U + n * kKeyMultiplier;
EXPECT_EQ(key, map.findKeyFor(values[n]));
}
}
TEST(UniqueIntegerMap, Del) {
MyMap map;
const size_t kCount = 100;
const size_t kKeyMultiplier = 3U; // must be >= 2.
uint32_t values[kCount];
for (size_t n = 0; n < kCount; ++n) {
uintptr_t key = 1U + n * kKeyMultiplier;
values[n] = map.add(key);
}
for (size_t n = 0; n < kCount; ++n) {
uintptr_t key = 1U + n * kKeyMultiplier;
map.del(key);
EXPECT_EQ(kCount - 1U - n, map.size());
EXPECT_EQ(0U, map.find(key));
EXPECT_EQ(0U, map.findKeyFor(values[n]));
}
EXPECT_TRUE(map.empty());
}
} // namespace emugl