<commit_before>#include <pthread.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/types.h>
#include <android/log.h>

#include "bin/src/halide_hexagon_remote.h"

namespace {

typedef halide_hexagon_remote_handle_t handle_t;
typedef halide_hexagon_remote_buffer buffer;

typedef int ion_user_handle_t;

struct ion_allocation_data {
	size_t len;
	size_t align;
	unsigned int heap_id_mask;
	unsigned int flags;
	ion_user_handle_t handle;
};

struct ion_fd_data {
	ion_user_handle_t handle;
	int fd;
};

struct ion_handle_data {
	ion_user_handle_t handle;
};

#define ION_IOC_ALLOC _IOWR('I', 0, ion_allocation_data)
#define ION_IOC_FREE _IOWR('I', 1, ion_handle_data)
#define ION_IOC_MAP _IOWR('I', 2, ion_fd_data)

ion_user_handle_t ion_alloc(int ion_fd, size_t len, size_t align, unsigned int heap_id_mask, unsigned int flags) {
    ion_allocation_data alloc = {
        len,
        align,
        heap_id_mask,
        flags,
        0
    };
    if (ioctl(ion_fd, ION_IOC_ALLOC, &alloc) < 0) {
        return -1;
    }
    return alloc.handle;
}

int ion_map(int ion_fd, ion_user_handle_t handle) {
    ion_fd_data data = {
        handle,
        0
    };
    if (ioctl(ion_fd, ION_IOC_MAP, &data) < 0) {
        return -1;
    }
    return data.fd;
}

int ion_free(int ion_fd, ion_user_handle_t ion_handle) {
    if(ioctl(ion_fd, ION_IOC_FREE, &ion_handle) < 0) {
        return -1;
    }
    return 0;
}

struct allocation_record {
    allocation_record *next;
    int ion_fd;
    ion_user_handle_t handle;
    int buf_fd;
    void *buf;
    size_t size;
};

// Make a dummy allocation so we don't need a special case for the head node.
allocation_record allocations = { NULL, };
pthread_mutex_t allocations_mutex;

}  // namespace

extern "C" {

__attribute__((weak)) void remote_register_buf(void* buf, int size, int fd);

void halide_hexagon_host_malloc_init() {
    pthread_mutex_init(&allocations_mutex, NULL);
}

void halide_hexagon_host_malloc_deinit() {
    pthread_mutex_destroy(&allocations_mutex);
}

void *halide_hexagon_host_malloc(size_t size) {
    int ion_fd = open("/dev/ion", O_RDONLY, 0);
    if (ion_fd < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "open('/dev/ion') failed");
        return NULL;
    }

    const int heap_id = 25;  // system heap
    const int ion_flags = 1;  // cached

    // Hexagon can only access a small number of mappings of these
    // sizes. We reduce the number of mappings required by aligning
    // large allocations to these sizes.
    static const size_t alignments[] = { 0x1000, 0x4000, 0x10000, 0x40000, 0x100000 };
    size_t alignment = alignments[0];

    // Align the size up to the minimum alignment.
    size = (size + alignment - 1) & ~(alignment - 1);

    if (heap_id != 25) {
        for (size_t i = 0; i < sizeof(alignments) / sizeof(alignments[0]); i++) {
            if (size >= alignments[i]) {
                alignment = alignments[i];
            }
        }
    }

    ion_user_handle_t handle = ion_alloc(ion_fd, size, alignment, 1 << heap_id, ion_flags);
    if (handle < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "ion_alloc(ion_fd, %d, %d, %d, %d) failed",
                            size, alignment, 1 << heap_id, ion_flags);
        close(ion_fd);
        return NULL;
    }

    // Map the ion handle to a file buffer.
    int buf_fd = ion_map(ion_fd, handle);
    if (buf_fd < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "ion_map(ion_fd, %d", handle);
        ion_free(ion_fd, handle);
        close(ion_fd);
        return NULL;
    }

    // Map the file buffer to a pointer.
    void *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, buf_fd, 0);
    if (buf == MAP_FAILED) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "mmap(NULL, %d, PROT_READ | PROT_WRITE, MAP_SHARED, %d, 0)",
                            size, buf_fd);
        ion_free(ion_fd, handle);
        close(ion_fd);
        return NULL;
    }

    // Register the buffer, so we get zero copy.
    if (remote_register_buf) {
        remote_register_buf(buf, size, buf_fd);
    }

    // Build a record for this allocation.
    allocation_record *rec = (allocation_record *)malloc(sizeof(allocation_record));
    if (!rec) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "malloc failed");
        munmap(buf, size);
        ion_free(ion_fd, handle);
        close(ion_fd);
        return NULL;
    }

    rec->next = NULL;
    rec->ion_fd = ion_fd;
    rec->handle = handle;
    rec->buf_fd = buf_fd;
    rec->buf = buf;
    rec->size = size;

    // Insert this record into the list of allocations. Insert it at
    // the front, since it's simpler, and most likely to be freed
    // next.
    pthread_mutex_lock(&allocations_mutex);
    rec->next = allocations.next;
    allocations.next = rec;
    pthread_mutex_unlock(&allocations_mutex);

    return buf;
}

void halide_hexagon_host_free(void *ptr) {
    if (!ptr) {
        return;
    }

    // Find the record for this allocation and remove it from the list.
    pthread_mutex_lock(&allocations_mutex);
    allocation_record *rec = &allocations;
    while (rec) {
        if (rec && rec->next->buf == ptr) {
            allocation_record *before_rec = rec;
            rec = before_rec->next;
            before_rec->next = before_rec->next->next;
            break;
        } else if (rec) {
            rec = rec->next;
        }
    }
    pthread_mutex_unlock(&allocations_mutex);
    if (!rec) {
        __android_log_print(ANDROID_LOG_WARN, "halide", "Allocation not found in allocation records");
        return;
    }

    // Unregister the buffer.
    if (remote_register_buf) {
        remote_register_buf(rec->buf, rec->size, -1);
    }

    // Unmap the memory
    munmap(rec->buf, rec->size);

    // free the ION allocation
    if (ion_free(rec->ion_fd, rec->handle) < 0) {
        __android_log_print(ANDROID_LOG_WARN, "halide", "ion_free(ion_fd, %d) failed", rec->handle);
    }

    // close the ion handle
    close(rec->ion_fd);

    free(rec);
}

// This is a shim for calling v2 from v1.
handle_t halide_hexagon_remote_get_symbol(handle_t module_ptr,
                                          const char* name, int nameLen) {
    handle_t sym = 0;
    int result = halide_hexagon_remote_get_symbol_v2(module_ptr, name, nameLen, &sym);
    return result == 0 ? sym : 0;
}

}  // extern "C"
<commit_msg>Add comments on ION allocator.<commit_after>#include <pthread.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <linux/types.h>
#include <android/log.h>

#include "bin/src/halide_hexagon_remote.h"

namespace {

typedef halide_hexagon_remote_handle_t handle_t;
typedef halide_hexagon_remote_buffer buffer;

// Allocations that are intended to be shared with Hexagon can be
// shared without copying if they are contiguous in physical
// memory. Android's ION allocator gives us a mechanism with which we
// can allocate contiguous physical memory.
typedef int ion_user_handle_t;

struct ion_allocation_data {
    size_t len;
    size_t align;
    unsigned int heap_id_mask;
    unsigned int flags;
    ion_user_handle_t handle;
};

struct ion_fd_data {
    ion_user_handle_t handle;
    int fd;
};

struct ion_handle_data {
    ion_user_handle_t handle;
};

#define ION_IOC_ALLOC _IOWR('I', 0, ion_allocation_data)
#define ION_IOC_FREE _IOWR('I', 1, ion_handle_data)
#define ION_IOC_MAP _IOWR('I', 2, ion_fd_data)

ion_user_handle_t ion_alloc(int ion_fd, size_t len, size_t align, unsigned int heap_id_mask, unsigned int flags) {
    ion_allocation_data alloc = {
        len,
        align,
        heap_id_mask,
        flags,
        0
    };
    if (ioctl(ion_fd, ION_IOC_ALLOC, &alloc) < 0) {
        return -1;
    }
    return alloc.handle;
}

int ion_map(int ion_fd, ion_user_handle_t handle) {
    ion_fd_data data = {
        handle,
        0
    };
    if (ioctl(ion_fd, ION_IOC_MAP, &data) < 0) {
        return -1;
    }
    return data.fd;
}

int ion_free(int ion_fd, ion_user_handle_t ion_handle) {
    if(ioctl(ion_fd, ION_IOC_FREE, &ion_handle) < 0) {
        return -1;
    }
    return 0;
}

// We need to be able to keep track of the size and some other
// information about ION allocations, so define a simple linked list
// of allocations we can traverse later.
struct allocation_record {
    allocation_record *next;
    int ion_fd;
    ion_user_handle_t handle;
    int buf_fd;
    void *buf;
    size_t size;
};

// Make a dummy allocation so we don't need a special case for the
// head list node.
allocation_record allocations = { NULL, };
pthread_mutex_t allocations_mutex;

}  // namespace

extern "C" {

// If this symbol is defined in the stub library we are going to link
// to, we need to call this in order to actually get zero copy
// behavior from our buffers.
__attribute__((weak)) void remote_register_buf(void* buf, int size, int fd);

void halide_hexagon_host_malloc_init() {
    pthread_mutex_init(&allocations_mutex, NULL);
}

void halide_hexagon_host_malloc_deinit() {
    pthread_mutex_destroy(&allocations_mutex);
}

void *halide_hexagon_host_malloc(size_t size) {
    int ion_fd = open("/dev/ion", O_RDONLY, 0);
    if (ion_fd < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "open('/dev/ion') failed");
        return NULL;
    }

    const int heap_id = 25;  // system heap
    const int ion_flags = 1;  // cached

    // Hexagon can only access a small number of mappings of these
    // sizes. We reduce the number of mappings required by aligning
    // large allocations to these sizes.
    static const size_t alignments[] = { 0x1000, 0x4000, 0x10000, 0x40000, 0x100000 };
    size_t alignment = alignments[0];

    // Align the size up to the minimum alignment.
    size = (size + alignment - 1) & ~(alignment - 1);

    if (heap_id != 25) {
        for (size_t i = 0; i < sizeof(alignments) / sizeof(alignments[0]); i++) {
            if (size >= alignments[i]) {
                alignment = alignments[i];
            }
        }
    }

    ion_user_handle_t handle = ion_alloc(ion_fd, size, alignment, 1 << heap_id, ion_flags);
    if (handle < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "ion_alloc(ion_fd, %d, %d, %d, %d) failed",
                            size, alignment, 1 << heap_id, ion_flags);
        close(ion_fd);
        return NULL;
    }

    // Map the ion handle to a file buffer.
    int buf_fd = ion_map(ion_fd, handle);
    if (buf_fd < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "ion_map(ion_fd, %d", handle);
        ion_free(ion_fd, handle);
        close(ion_fd);
        return NULL;
    }

    // Map the file buffer to a pointer.
    void *buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, buf_fd, 0);
    if (buf == MAP_FAILED) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "mmap(NULL, %d, PROT_READ | PROT_WRITE, MAP_SHARED, %d, 0)",
                            size, buf_fd);
        ion_free(ion_fd, handle);
        close(ion_fd);
        return NULL;
    }

    // Register the buffer, so we get zero copy.
    if (remote_register_buf) {
        remote_register_buf(buf, size, buf_fd);
    }

    // Build a record for this allocation.
    allocation_record *rec = (allocation_record *)malloc(sizeof(allocation_record));
    if (!rec) {
        __android_log_print(ANDROID_LOG_ERROR, "halide", "malloc failed");
        munmap(buf, size);
        ion_free(ion_fd, handle);
        close(ion_fd);
        return NULL;
    }

    rec->next = NULL;
    rec->ion_fd = ion_fd;
    rec->handle = handle;
    rec->buf_fd = buf_fd;
    rec->buf = buf;
    rec->size = size;

    // Insert this record into the list of allocations. Insert it at
    // the front, since it's simpler, and most likely to be freed
    // next.
    pthread_mutex_lock(&allocations_mutex);
    rec->next = allocations.next;
    allocations.next = rec;
    pthread_mutex_unlock(&allocations_mutex);

    return buf;
}

void halide_hexagon_host_free(void *ptr) {
    if (!ptr) {
        return;
    }

    // Find the record for this allocation and remove it from the list.
    pthread_mutex_lock(&allocations_mutex);
    allocation_record *rec = &allocations;
    while (rec) {
        if (rec && rec->next->buf == ptr) {
            allocation_record *before_rec = rec;
            rec = before_rec->next;
            before_rec->next = before_rec->next->next;
            break;
        } else if (rec) {
            rec = rec->next;
        }
    }
    pthread_mutex_unlock(&allocations_mutex);
    if (!rec) {
        __android_log_print(ANDROID_LOG_WARN, "halide", "Allocation not found in allocation records");
        return;
    }

    // Unregister the buffer.
    if (remote_register_buf) {
        remote_register_buf(rec->buf, rec->size, -1);
    }

    // Unmap the memory
    munmap(rec->buf, rec->size);

    // free the ION allocation
    if (ion_free(rec->ion_fd, rec->handle) < 0) {
        __android_log_print(ANDROID_LOG_WARN, "halide", "ion_free(ion_fd, %d) failed", rec->handle);
    }

    // close the ion handle
    close(rec->ion_fd);

    free(rec);
}

// This is a shim for calling v2 from v1.
handle_t halide_hexagon_remote_get_symbol(handle_t module_ptr,
                                          const char* name, int nameLen) {
    handle_t sym = 0;
    int result = halide_hexagon_remote_get_symbol_v2(module_ptr, name, nameLen, &sym);
    return result == 0 ? sym : 0;
}

}  // extern "C"
<|endoftext|>


<commit_before>inline IBOID afCreateTiledPlaneIBO(int numTiles, int* numIndies)
{
	const int numVert = numTiles + 1;

	std::vector<AFIndex> indi;

	for (int y = 0; y < numTiles; y++)
	{
		if (y != 0)
		{
			indi.push_back(AFIndex(y * numVert));
		}
		indi.push_back(AFIndex(y * numVert));
		for (int x = 0; x < numTiles; x++)
		{
			indi.push_back(AFIndex((y + 1) * numVert + x));
			indi.push_back(AFIndex(y * numVert + x + 1));
		}
		indi.push_back(AFIndex((y + 1) * numVert + numVert - 1));
		if (y != numTiles - 1)
		{
			indi.push_back(AFIndex((y + 1) * numVert + numVert - 1));
		}
	}

	if (numIndies)
	{
		*numIndies = (int)indi.size();
	}

	return afCreateIndexBuffer((int)indi.size(), &indi[0]);
}

inline VBOID afCreateTiledPlaneVBO(int numTiles)
{
	std::vector<Vec2> v;
	for (int y = 0; y <= numTiles; y++) {
		for (int x = 0; x <= numTiles; x++) {
			v.push_back((Vec2)IVec2(x, y) / (float)numTiles * 2 - Vec2(1, 1));
		}
	}
	return afCreateVertexBuffer((int)v.size() * sizeof(v[0]), &v[0]);
}

inline IBOID afCreateQuadListIndexBuffer(int numQuads)
{
	std::vector<AFIndex> indi;
	int numIndi = numQuads * 6;
	indi.resize(numIndi);
	for (int i = 0; i < numIndi; i++)
	{
		static int tbl[] = { 0, 1, 2, 1, 3, 2 };
		int rectIdx = i / 6;
		int vertIdx = i % 6;
		indi[i] = AFIndex(rectIdx * 4 + tbl[vertIdx]);
	}
	return afCreateIndexBuffer(numIndi, &indi[0]);
}

struct DDSHeader {
	uint32_t h3[3];
	int h, w;
	uint32_t h2[2];
	int mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2;
	bool IsCubeMap() const { return caps2 == 0xFE00; }
	int GetArraySize() const { return IsCubeMap() ? 6 : 1; }
	int GetMipCnt() const { return std::max(mipCnt, 1); }
};

inline void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

inline AFFormat ArrangeRawDDS(void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
#ifdef _MSC_VER	// GL_EXT_texture_format_BGRA8888 is an extension for OpenGL ES, so should use this only on Windows.
	if (hdr->rMask == 0xff0000 && hdr->gMask == 0x00ff00 && hdr->bMask == 0x0000ff)
	{
		return AFF_B8G8R8A8_UNORM;
	}
#endif
	if (hdr->rMask == 0x0000ff && hdr->gMask == 0x00ff00 && hdr->bMask == 0xff0000)
	{
		return AFF_R8G8B8A8_UNORM;
	}
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	std::for_each((uint32_t*)img + 128 / 4, (uint32_t*)img + size / 4, [&](uint32_t& im) {
		im = ((hdr->aMask & im) >> aShift << 24) + ((hdr->bMask & im) >> bShift << 16) + ((hdr->gMask & im) >> gShift << 8) + ((hdr->rMask & im) >> rShift);
		if (hdr->aMask == 0) {
			im |= 0xff000000;
		}
	});
	return AFF_R8G8B8A8_UNORM;
}

inline SRVID afLoadDDSTexture(const char* name, TexDesc& texDesc)
{
	int fileSize;
	void* img = LoadFile(name, &fileSize);
	if (!img) {
		aflog("afLoadDDSTexture failed! %s", name);
		return SRVID();
	}
	const DDSHeader* hdr = (DDSHeader*)img;

	AFFormat format = AFF_INVALID;
	int(*pitchCalcurator)(int, int) = nullptr;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = AFF_BC1_UNORM;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 8; };
		break;
	case 0x33545844: //'3TXD':
		format = AFF_BC2_UNORM;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	case 0x35545844: //'5TXD':
		format = AFF_BC3_UNORM;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	default:
		format = ArrangeRawDDS(img, fileSize);
		pitchCalcurator = [](int w, int h) { return w * h * 4; };
		break;
	}
	texDesc.size.x = hdr->w;
	texDesc.size.y = hdr->h;
	texDesc.arraySize = hdr->GetArraySize();
	texDesc.isCubeMap = hdr->IsCubeMap();

	int arraySize = hdr->GetArraySize();
	int mipCnt = hdr->GetMipCnt();
	std::vector<AFTexSubresourceData> r;
	int offset = 128;
	for (int a = 0; a < arraySize; a++) {
		for (int m = 0; m < mipCnt; m++) {
			int w = std::max(1, hdr->w >> m);
			int h = std::max(1, hdr->h >> m);
			int pitch = pitchCalcurator(w, h);
			r.push_back({ (char*)img + offset, (uint32_t)pitchCalcurator(w, 1), (uint32_t)pitch });
			offset += pitch;
		}
	}

	SRVID srv = afCreateTexture2D(format, texDesc, mipCnt, &r[0]);
	assert(srv);
	free(img);
	return srv;
}

SRVID LoadTextureViaOS(const char* name, IVec2& size);

inline SRVID afLoadTexture(const char* name, TexDesc& desc)
{
	desc = TexDesc();
	size_t len = strlen(name);
	SRVID tex;
	if (len > 4 && !stricmp(name + len - 4, ".dds"))
	{
		tex = afLoadDDSTexture(name, desc);
	} else
	{
		tex = LoadTextureViaOS(name, desc.size);
	}
	afSetTextureName(tex, name);
	return tex;
}

inline SRVID afLoadTexture(const char* name)
{
	TexDesc dummy;
	return afLoadTexture(name, dummy);
}
<commit_msg>Support 24 bpp DDS files.<commit_after>inline IBOID afCreateTiledPlaneIBO(int numTiles, int* numIndies)
{
	const int numVert = numTiles + 1;

	std::vector<AFIndex> indi;

	for (int y = 0; y < numTiles; y++)
	{
		if (y != 0)
		{
			indi.push_back(AFIndex(y * numVert));
		}
		indi.push_back(AFIndex(y * numVert));
		for (int x = 0; x < numTiles; x++)
		{
			indi.push_back(AFIndex((y + 1) * numVert + x));
			indi.push_back(AFIndex(y * numVert + x + 1));
		}
		indi.push_back(AFIndex((y + 1) * numVert + numVert - 1));
		if (y != numTiles - 1)
		{
			indi.push_back(AFIndex((y + 1) * numVert + numVert - 1));
		}
	}

	if (numIndies)
	{
		*numIndies = (int)indi.size();
	}

	return afCreateIndexBuffer((int)indi.size(), &indi[0]);
}

inline VBOID afCreateTiledPlaneVBO(int numTiles)
{
	std::vector<Vec2> v;
	for (int y = 0; y <= numTiles; y++) {
		for (int x = 0; x <= numTiles; x++) {
			v.push_back((Vec2)IVec2(x, y) / (float)numTiles * 2 - Vec2(1, 1));
		}
	}
	return afCreateVertexBuffer((int)v.size() * sizeof(v[0]), &v[0]);
}

inline IBOID afCreateQuadListIndexBuffer(int numQuads)
{
	std::vector<AFIndex> indi;
	int numIndi = numQuads * 6;
	indi.resize(numIndi);
	for (int i = 0; i < numIndi; i++)
	{
		static int tbl[] = { 0, 1, 2, 1, 3, 2 };
		int rectIdx = i / 6;
		int vertIdx = i % 6;
		indi[i] = AFIndex(rectIdx * 4 + tbl[vertIdx]);
	}
	return afCreateIndexBuffer(numIndi, &indi[0]);
}

struct DDSHeader
{
	uint32_t h3[3];
	int h, w;
	uint32_t h2[2];
	int mipCnt;
	uint32_t h13[13];
	uint32_t fourcc, bitsPerPixel, rMask, gMask, bMask, aMask, caps1, caps2;
	bool IsCubeMap() const { return caps2 == 0xFE00; }
	int GetArraySize() const { return IsCubeMap() ? 6 : 1; }
	int GetMipCnt() const { return std::max(mipCnt, 1); }
};

inline void bitScanForward(uint32_t* result, uint32_t mask)
{
	//	DWORD dwd;
	//	_BitScanForward(&dwd, mask);
	//	*result = dwd;

	for (int i = 0; i < 32; i++) {
		if (mask & (1 << i)) {
			*result = i;
			return;
		}
	}
	*result = 0;
}

inline AFFormat ArrangeRawDDS(void* img, int size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	assert(hdr->bitsPerPixel == 32);
#ifdef _MSC_VER	// GL_EXT_texture_format_BGRA8888 is an extension for OpenGL ES, so should use this only on Windows.
	if (hdr->rMask == 0xff0000 && hdr->gMask == 0x00ff00 && hdr->bMask == 0x0000ff)
	{
		return AFF_B8G8R8A8_UNORM;
	}
#endif
	if (hdr->rMask == 0x0000ff && hdr->gMask == 0x00ff00 && hdr->bMask == 0xff0000)
	{
		return AFF_R8G8B8A8_UNORM;
	}
	uint32_t rShift, gShift, bShift, aShift;
	bitScanForward(&rShift, hdr->rMask);
	bitScanForward(&gShift, hdr->gMask);
	bitScanForward(&bShift, hdr->bMask);
	bitScanForward(&aShift, hdr->aMask);
	std::for_each((uint32_t*)img + 128 / 4, (uint32_t*)img + size / 4, [&](uint32_t& im) {
		im = ((hdr->aMask & im) >> aShift << 24) + ((hdr->bMask & im) >> bShift << 16) + ((hdr->gMask & im) >> gShift << 8) + ((hdr->rMask & im) >> rShift);
		if (hdr->aMask == 0) {
			im |= 0xff000000;
		}
	});
	return AFF_R8G8B8A8_UNORM;
}

inline void Convert24To32(void*& img, int& size)
{
	const DDSHeader* hdr = (DDSHeader*)img;
	if (hdr->bitsPerPixel != 24)
	{
		return;
	}
	int numPixels = (size - 128) / 3;
	int newSize = 128 + numPixels * 4;
	void* newImg = malloc(newSize);
	*(DDSHeader*)newImg = *hdr;
	((DDSHeader*)newImg)->bitsPerPixel = 32;
	uint8_t* dst = (uint8_t*)newImg + sizeof(DDSHeader);
	uint8_t* src = (uint8_t*)img + sizeof(DDSHeader);
	for (int i = 0; i < numPixels; i++)
	{
		*dst++ = *src++;
		*dst++ = *src++;
		*dst++ = *src++;
		*dst++ = 255;
	}
	free(img);
	img = newImg;
	size = newSize;
}

inline SRVID afLoadDDSTexture(const char* name, TexDesc& texDesc)
{
	int fileSize;
	void* img = LoadFile(name, &fileSize);
	if (!img) {
		aflog("afLoadDDSTexture failed! %s", name);
		return SRVID();
	}
	Convert24To32(img, fileSize);
	const DDSHeader* hdr = (DDSHeader*)img;

	AFFormat format = AFF_INVALID;
	int(*pitchCalcurator)(int, int) = nullptr;
	switch (hdr->fourcc) {
	case 0x31545844: //'1TXD':
		format = AFF_BC1_UNORM;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 8; };
		break;
	case 0x33545844: //'3TXD':
		format = AFF_BC2_UNORM;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	case 0x35545844: //'5TXD':
		format = AFF_BC3_UNORM;
		pitchCalcurator = [](int w, int h) { return ((w + 3) / 4) * ((h + 3) / 4) * 16; };
		break;
	default:
		format = ArrangeRawDDS(img, fileSize);
		pitchCalcurator = [](int w, int h) { return w * h * 4; };
		break;
	}
	texDesc.size.x = hdr->w;
	texDesc.size.y = hdr->h;
	texDesc.arraySize = hdr->GetArraySize();
	texDesc.isCubeMap = hdr->IsCubeMap();

	int arraySize = hdr->GetArraySize();
	int mipCnt = hdr->GetMipCnt();
	std::vector<AFTexSubresourceData> r;
	int offset = 128;
	for (int a = 0; a < arraySize; a++) {
		for (int m = 0; m < mipCnt; m++) {
			int w = std::max(1, hdr->w >> m);
			int h = std::max(1, hdr->h >> m);
			int pitch = pitchCalcurator(w, h);
			r.push_back({ (char*)img + offset, (uint32_t)pitchCalcurator(w, 1), (uint32_t)pitch });
			offset += pitch;
		}
	}
	assert(offset <= fileSize);

	SRVID srv = afCreateTexture2D(format, texDesc, mipCnt, &r[0]);
	assert(srv);
	free(img);
	return srv;
}

SRVID LoadTextureViaOS(const char* name, IVec2& size);

inline SRVID afLoadTexture(const char* name, TexDesc& desc)
{
	desc = TexDesc();
	size_t len = strlen(name);
	SRVID tex;
	if (len > 4 && !stricmp(name + len - 4, ".dds"))
	{
		tex = afLoadDDSTexture(name, desc);
	}
	else
	{
		tex = LoadTextureViaOS(name, desc.size);
	}
	afSetTextureName(tex, name);
	return tex;
}

inline SRVID afLoadTexture(const char* name)
{
	TexDesc dummy;
	return afLoadTexture(name, dummy);
}
<|endoftext|>


<commit_before>/**
 * This is the main entry point in the SparkFun Autonomous Vehicle Competition (AVC)
 * for the 2015 robot.
 */

#include "CommandSequence.h"
#include "Timon.h"
#include "UserLeds.h"

#include <BlackGPIO.h>

#include <cmath>
#include <iostream>

#include <signal.h>

using namespace avc;
using namespace std;

namespace {
  // Flag will be set when user terminates via ^C or uses kill on process
  bool hasBeenInterrupted = false;

  void interrupted(int sig) {
    hasBeenInterrupted = true;
  }
}

//
// Main entry point into the code basically waits for user
// to press button on BBB then runs the autonomous code
//
int main(int argc, const char** argv) {
  signal(SIGINT, interrupted);
  signal(SIGTERM, interrupted);
  UserLeds& leds = UserLeds::getInstance();
  int waitCnt = 0;

  // Create instance of vehicle
  Timon timon;

  // Start button connected to P9 18 (GPIO_4)
  BlackLib::BlackGPIO longButton(BlackLib::GPIO_4, BlackLib::input);
  // Extra mode start button connected to P9 24 (GPIO_15)
  BlackLib::BlackGPIO shortButton(BlackLib::GPIO_15, BlackLib::input);

  bool longWasHigh = longButton.isHigh();
  bool shortWasHigh = shortButton.isHigh();

  cout << "Entering main loop - waiting for trigger ...\n";

  while (hasBeenInterrupted == false) {

    bool shortIsHigh = shortButton.isHigh();
    bool longIsHigh = longButton.isHigh();

    // Run auton when button is pressed and then released
    if ((longWasHigh == true) && (longIsHigh == false)) {

      leds.setState(0xf);
      cout << "Starting auton for long path around track\n";
      timon.setAutonLongWay();

      Timer autonTimer;
      Command::run(timon);

      cout << "Long path auton completed in " << autonTimer.secsElapsed() << " seconds\n";
      timon.disable();

    } else if ((shortWasHigh == true) && (shortIsHigh == false)) {

      leds.setState(0xf);
      cout << "Starting auton for short path around track\n";
      timon.setAutonShortWay();

      Timer autonTimer;
      Command::run(timon);

      cout << "Short path auton completed in " << autonTimer.secsElapsed() << " seconds\n";
      timon.disable();

    } else {
      Timer::sleep(0.05);
      int ledState = waitCnt & 0xf;
      leds.setState(ledState);
      if (ledState == 0) {
	waitCnt = 1;
      } else {
	waitCnt <<= 1;
      }
    }
    // Save prior state
    longWasHigh = longIsHigh;
    shortWasHigh = shortIsHigh;
  }

  cout << "Terminated by external signal\n";

  return 0;
}

//
// Implementation of the Timon class methods
//

Timon::Timon() :
  CommandParallel("Timon", true),
#if USE_SERVOS
  _left(LEFT_PWM, -1.0, +1.0),
  _right(RIGHT_PWM, -1.0, +1.0),
#else
  _left(LEFT_PWM, LEFT_GPIO_FWD, LEFT_GPIO_REV),
  _right(RIGHT_PWM, RIGHT_GPIO_FWD, RIGHT_GPIO_REV),
#endif
  _gyro(),
  _initHeading(0),
  _heading(0),
  _wayPoint(1),
  _crashed(false),
  _done(false)
{
  if (!_gyro.reset()) {
    cerr << "**ERROR*** Failed to reset gyro\n";
    _crashed = true;
  }

}

void Timon::setAutonLongWay() {
  clear();
  const float drivePow = 0.2;

  CommandSequence* drive = new CommandSequence("Drive");
  // Give .25 seconds to let user move hand away
  drive->add(new DrivePowerTime(*this, 0, 0, 0.25));
  // Short drive to first corner
  drive->add(new DriveToTurn(*this, drivePow, 1.0));
  // Give 1/2 second to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, 0.5));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Long drive to second corner
  drive->add(new DriveToTurn(*this, drivePow, 3.0));
  // Give 1/2 second to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, 0.5));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Medium drive to third corner
  drive->add(new DriveToTurn(*this, drivePow, 2.0));
  // Give 1/2 second to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, 0.5));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Long drive to fourth corner
  drive->add(new DriveToTurn(*this, drivePow, 3.0));
  // Give 1/2 second to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, 0.5));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Short drive to finish line
  drive->add(new DriveToTurn(*this, drivePow, 1.0));
  // And stop (NOTE: this is optional now as the Timon class should
  // automatically disable everything after finishing an auton run)
  drive->add(DrivePowerTime::createStopCommand(*this));
  add(drive);
}

void Timon::setAutonShortWay() {
  clear();

  CommandSequence* drive = new CommandSequence("Drive");
  // Give .25 seconds to let user move hand away
  drive->add(new DrivePowerTime(*this, 0, 0, 0.25));

  // Uncomment to spin left side forward one second followed
  // by right side forward for a second to verify logic is right
  //drive->add(new DrivePowerTime(*this, 0.2, 0, 1.0));
  //drive->add(new DrivePowerTime(*this, 0, .2, 1.0));

  // Make a left hand turn
  drive->add(new MakeTurn(*this, -90.0));

  add(drive);
}

Timon::~Timon() {
  _left.set(0);
  _right.set(0);
  _left.disable();
  _right.disable();
}

void Timon::doInitialize() {
  _crashed = _done = false;
  _wayPoint = 1;

  if (!_gyro.getHeading(_initHeading)) {
    _crashed = true;
    cerr << "***ERROR*** Gyro not responding (unable to read heading)\n";
  }

  CommandParallel::doInitialize();
}

void Timon::readSensors() {
  float heading;
  if (_gyro.getHeading(heading)) {
    heading -= _initHeading;
    if (heading < 0) {
      heading += 360.0;
    }
    _heading = heading;
  } else {
    _crashed = true;
    cerr << "***ERROR*** Gyro not responding (unable to read heading)\n";
  }
}

float Timon::getRelativeHeading(float initHeading) const {
  float relHeading = _heading - initHeading;
  // Put in range of [0, 360]
  if (relHeading < 0) {
    relHeading += 360;
  }
  // Now adjust to range of [-180.0, +180.0]
  if (relHeading > 180) {
    relHeading -= 360;
  }
  return relHeading;
}

Command::State Timon::doExecute() {
  readSensors();
  if (hasCrashed()) {
    // Make sure motors are turned off
    coast();
    // Catastrophic failure!
    return Command::INTERRUPTED;
  }

  return CommandParallel::doExecute();
}

void Timon::doEnd(Command::State reason) {
  CommandParallel::doEnd(reason);
  disable();
}

void Timon::disable() {
  coast();
}

void Timon::nextWaypoint() {
  UserLeds& leds = UserLeds::getInstance();
  leds.setState(_wayPoint++);
}

float Timon::rangeCheckPower(float power) {
  return max(-1.0f, min(+1.0f, power));
}

ostream& Timon::print(std::ostream& out, const Command& cmd) const {
  out << cmd << "  Timon(left=" << _left.get() << ", right="
      << _right.get() << ", heading=" << _heading << ")";
  return out;
}

//
// Implementation of the DriveToTurn class methods
//

DriveToTurn::DriveToTurn(Timon& car, float power, float timeout) :
  Command("DriveToTurn", timeout),
  _car(car),
  _power(power),
  _initialHeading(0)
{
}

void DriveToTurn::doInitialize() {
  _initialHeading = _car.getHeading();
  _car.print(cout, *this) << "\n";
}

Command::State DriveToTurn::doExecute() {
  float turned = _car.getRelativeHeading(_initialHeading);
  float turnedMag = abs(turned);

  float powerCorrect = 0;
  if (turnedMag > 1.0) {
    // TODO: Figure out a power correction (basically P of PID) based
    // on how far turn is off
    powerCorrect = turnedMag / 180 + 0.005;
    powerCorrect = (turned > 0) ? powerCorrect : -powerCorrect;
    cout << "Off course: " << turned << " degrees, power correct: "
	 << powerCorrect << "\n";
  }

  float powerLeft = _power - powerCorrect;
  float powerRight = _power + powerCorrect;

  // Seek new drive power levels with power correction for steering
  powerLeft = Timon::rangeCheckPower(powerLeft);
  powerRight = Timon::rangeCheckPower(powerRight);
  _car.seekDrive(powerLeft, powerRight);

  bool foundCorner = _car.detectedCorner();
  // TODO: Remove this time out override once we can detect the corner
  foundCorner = ((getElapsedTime() / getTimeout()) >= 0.5);
  _car.print(cout, *this) << "\n";
  return (foundCorner ? Command::NORMAL_END : Command::STILL_RUNNING);
}

void DriveToTurn::doEnd(Command::State reason) {
  _car.print(cout, *this) << "\n";
  _car.nextWaypoint();
}

//
// Implementation of the MakeTurn class methods
//

MakeTurn::MakeTurn(Timon& car, float turn) :
  Command("MakeTurn", 1.0 + abs(turn) / 25),
  _car(car),
  _turn(turn),
  _initialHeading(0),
  _lastErr(0),
  _inRangeCnt(0)
{
}

MakeTurn::~MakeTurn() {
}

void MakeTurn::doInitialize() {
  _car.print(cout, *this) << "\n";
  _initialHeading = _car.getHeading();
  _lastErr = _turn;
  _inRangeCnt = 0;
}

Command::State MakeTurn::doExecute() {
  float carTurned = _car.getRelativeHeading(_initialHeading);
  float err = _turn - carTurned;
  float deltaErr = _lastErr - err;
  const float P = (0.15f * 10.0f / 360.0f);
  const float D = (0.1f * 10.0f / 360.0f);

  // TODO: Delete once we figure out how to get angle (the
  // statement below makes
  //float percentTimeElapsed = getElapsedTime() / getTimeout();
  //err = (percentTimeElapsed >= 0.5) ? 0.0 : (1.1 - percentTimeElapsed) * _turn;

  float steer = err * P + deltaErr * D;
  float steerMag = abs(steer);
  const float minMag = 0.2f;
  if (steerMag < minMag) {
    steer = (steer < 0) ? -minMag : minMag;
  }

  // Limit to .35 power level
  const float maxMag = 0.35f;
  steer = min(maxMag, max(-maxMag, steer));

  steer = Timon::rangeCheckPower(steer);
  _car.drive(steer, -steer);

  // TODO: NOTE, this implementation does not take into account a minimum
  // power to turn the car (for example, if we get within 10 degrees and
  // drop the power too low, the car may stop turning and never reach
  // the final target).

  _car.print(cout, *this) << "  turned: " << carTurned << "  err: " << err << "\n";

  _lastErr = err;

  // Done if within 3 degrees
  if (abs(err) < 3.0) {
    _inRangeCnt++;
  } else {
    _inRangeCnt = 0;
  }
  return ((_inRangeCnt >= 3) ? Command::NORMAL_END : Command::STILL_RUNNING);
}

void MakeTurn::doEnd(Command::State reason) {
  _car.print(cout, *this) << "\n";
}

//
// Implementation of the DrivePowerTime class methods
//

DrivePowerTime::DrivePowerTime(Timon& car, float powerLeft, float powerRight,
			       float howLong) :
  Command("DrivePowerTime", howLong + 1.0),
  _car(car),
  _powerLeft(powerLeft),
  _powerRight(powerRight),
  _runTime(howLong)
{
}

DrivePowerTime::~DrivePowerTime() {
}

Command::State DrivePowerTime::doExecute() {
  if (getElapsedTime() >= _runTime) {
    return Command::NORMAL_END;
  }

  _car.seekDrive(_powerLeft, _powerRight);
  return Command::STILL_RUNNING;
}

void DrivePowerTime::doEnd(Command::State reason) {
  // If command was to bring car to a stop and the
  if (reason == Command::NORMAL_END) {
    if (_powerLeft == 0 && _powerRight == 0) {
      _car.drive(0, 0);
    }
  }
}
<commit_msg>Playing with PID values for turn and giving more time to slow down<commit_after>/**
 * This is the main entry point in the SparkFun Autonomous Vehicle Competition (AVC)
 * for the 2015 robot.
 */

#include "CommandSequence.h"
#include "Timon.h"
#include "UserLeds.h"

#include <BlackGPIO.h>

#include <cmath>
#include <iostream>

#include <signal.h>

using namespace avc;
using namespace std;

namespace {
  // Flag will be set when user terminates via ^C or uses kill on process
  bool hasBeenInterrupted = false;

  void interrupted(int sig) {
    hasBeenInterrupted = true;
  }
}

//
// Main entry point into the code basically waits for user
// to press button on BBB then runs the autonomous code
//
int main(int argc, const char** argv) {
  signal(SIGINT, interrupted);
  signal(SIGTERM, interrupted);
  UserLeds& leds = UserLeds::getInstance();
  int waitCnt = 0;

  // Create instance of vehicle
  Timon timon;

  // Start button connected to P9 18 (GPIO_4)
  BlackLib::BlackGPIO longButton(BlackLib::GPIO_4, BlackLib::input);
  // Extra mode start button connected to P9 24 (GPIO_15)
  BlackLib::BlackGPIO shortButton(BlackLib::GPIO_15, BlackLib::input);

  bool longWasHigh = longButton.isHigh();
  bool shortWasHigh = shortButton.isHigh();

  cout << "Entering main loop - waiting for trigger ...\n";

  while (hasBeenInterrupted == false) {

    bool shortIsHigh = shortButton.isHigh();
    bool longIsHigh = longButton.isHigh();

    // Run auton when button is pressed and then released
    if ((longWasHigh == true) && (longIsHigh == false)) {

      leds.setState(0xf);
      cout << "Starting auton for long path around track\n";
      timon.setAutonLongWay();

      Timer autonTimer;
      Command::run(timon);

      cout << "Long path auton completed in " << autonTimer.secsElapsed() << " seconds\n";
      timon.disable();

    } else if ((shortWasHigh == true) && (shortIsHigh == false)) {

      leds.setState(0xf);
      cout << "Starting auton for short path around track\n";
      timon.setAutonShortWay();

      Timer autonTimer;
      Command::run(timon);

      cout << "Short path auton completed in " << autonTimer.secsElapsed() << " seconds\n";
      timon.disable();

    } else {
      Timer::sleep(0.05);
      int ledState = waitCnt & 0xf;
      leds.setState(ledState);
      if (ledState == 0) {
	waitCnt = 1;
      } else {
	waitCnt <<= 1;
      }
    }
    // Save prior state
    longWasHigh = longIsHigh;
    shortWasHigh = shortIsHigh;
  }

  cout << "Terminated by external signal\n";

  return 0;
}

//
// Implementation of the Timon class methods
//

Timon::Timon() :
  CommandParallel("Timon", true),
#if USE_SERVOS
  _left(LEFT_PWM, -1.0, +1.0),
  _right(RIGHT_PWM, -1.0, +1.0),
#else
  _left(LEFT_PWM, LEFT_GPIO_FWD, LEFT_GPIO_REV),
  _right(RIGHT_PWM, RIGHT_GPIO_FWD, RIGHT_GPIO_REV),
#endif
  _gyro(),
  _initHeading(0),
  _heading(0),
  _wayPoint(1),
  _crashed(false),
  _done(false)
{
  if (!_gyro.reset()) {
    cerr << "**ERROR*** Failed to reset gyro\n";
    _crashed = true;
  }

}

void Timon::setAutonLongWay() {
  clear();
  const float drivePow = 0.2;
  const float stopSecs = 2.0;

  // Max seconds to drive on short and long sides
  const float shortSide = 2.0;
  const float longSide = 3.0;

  CommandSequence* drive = new CommandSequence("Drive");
  // Give .25 seconds to let user move hand away
  drive->add(new DrivePowerTime(*this, 0, 0, 0.25));
  // Short drive to first corner
  drive->add(new DriveToTurn(*this, drivePow, shortSide / 2));
  // Give time to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, stopSecs));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Long drive to second corner
  drive->add(new DriveToTurn(*this, drivePow, longSide));
  // Give time to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, stopSecs));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Medium drive to third corner
  drive->add(new DriveToTurn(*this, drivePow, shortSide));
  // Give time to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, stopSecs));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Long drive to fourth corner
  drive->add(new DriveToTurn(*this, drivePow, longSide));
  // Give time to slow down
  drive->add(new DrivePowerTime(*this, 0, 0, stopSecs));
  // Make a right hand turn
  drive->add(new MakeTurn(*this, 90.0));
  // Short drive to finish line (and a bit more to cross it)
  drive->add(new DriveToTurn(*this, drivePow, shortSide / 2 + shortSide / 10));
  // And stop (NOTE: this is optional now as the Timon class should
  // automatically disable everything after finishing an auton run)
  drive->add(DrivePowerTime::createStopCommand(*this));
  add(drive);
}

void Timon::setAutonShortWay() {
  clear();

  CommandSequence* drive = new CommandSequence("Drive");
  // Give .25 seconds to let user move hand away
  drive->add(new DrivePowerTime(*this, 0, 0, 0.25));

  // Uncomment to spin left side forward one second followed
  // by right side forward for a second to verify logic is right
  //drive->add(new DrivePowerTime(*this, 0.2, 0, 1.0));
  //drive->add(new DrivePowerTime(*this, 0, .2, 1.0));

  // Make a left hand turn
  drive->add(new MakeTurn(*this, -90.0));

  add(drive);
}

Timon::~Timon() {
  _left.set(0);
  _right.set(0);
  _left.disable();
  _right.disable();
}

void Timon::doInitialize() {
  _crashed = _done = false;
  _wayPoint = 1;

  if (!_gyro.getHeading(_initHeading)) {
    _crashed = true;
    cerr << "***ERROR*** Gyro not responding (unable to read heading)\n";
  }

  CommandParallel::doInitialize();
}

void Timon::readSensors() {
  float heading;
  if (_gyro.getHeading(heading)) {
    heading -= _initHeading;
    if (heading < 0) {
      heading += 360.0;
    }
    _heading = heading;
  } else {
    _crashed = true;
    cerr << "***ERROR*** Gyro not responding (unable to read heading)\n";
  }
}

float Timon::getRelativeHeading(float initHeading) const {
  float relHeading = _heading - initHeading;
  // Put in range of [0, 360]
  if (relHeading < 0) {
    relHeading += 360;
  }
  // Now adjust to range of [-180.0, +180.0]
  if (relHeading > 180) {
    relHeading -= 360;
  }
  return relHeading;
}

Command::State Timon::doExecute() {
  readSensors();
  if (hasCrashed()) {
    // Make sure motors are turned off
    coast();
    // Catastrophic failure!
    return Command::INTERRUPTED;
  }

  return CommandParallel::doExecute();
}

void Timon::doEnd(Command::State reason) {
  CommandParallel::doEnd(reason);
  disable();
}

void Timon::disable() {
  coast();
}

void Timon::nextWaypoint() {
  UserLeds& leds = UserLeds::getInstance();
  leds.setState(_wayPoint++);
}

float Timon::rangeCheckPower(float power) {
  return max(-1.0f, min(+1.0f, power));
}

ostream& Timon::print(std::ostream& out, const Command& cmd) const {
  out << cmd << "  Timon(left=" << _left.get() << ", right="
      << _right.get() << ", heading=" << _heading << ")";
  return out;
}

//
// Implementation of the DriveToTurn class methods
//

DriveToTurn::DriveToTurn(Timon& car, float power, float timeout) :
  Command("DriveToTurn", timeout),
  _car(car),
  _power(power),
  _initialHeading(0)
{
}

void DriveToTurn::doInitialize() {
  _initialHeading = _car.getHeading();
  _car.print(cout, *this) << "\n";
}

Command::State DriveToTurn::doExecute() {
  float turned = _car.getRelativeHeading(_initialHeading);
  float turnedMag = abs(turned);

  float powerCorrect = 0;
  if (turnedMag > 1.0) {
    // TODO: Figure out a power correction (basically P of PID) based
    // on how far turn is off
    powerCorrect = turnedMag / 180 + 0.005;
    powerCorrect = (turned > 0) ? powerCorrect : -powerCorrect;
    cout << "Off course: " << turned << " degrees, power correct: "
	 << powerCorrect << "\n";
  }

  float powerLeft = _power - powerCorrect;
  float powerRight = _power + powerCorrect;

  // Seek new drive power levels with power correction for steering
  powerLeft = Timon::rangeCheckPower(powerLeft);
  powerRight = Timon::rangeCheckPower(powerRight);
  _car.seekDrive(powerLeft, powerRight);

  bool foundCorner = _car.detectedCorner();
  // TODO: Remove this time out override once we can detect the corner
  foundCorner = ((getElapsedTime() / getTimeout()) >= 0.5);
  _car.print(cout, *this) << "\n";
  return (foundCorner ? Command::NORMAL_END : Command::STILL_RUNNING);
}

void DriveToTurn::doEnd(Command::State reason) {
  _car.print(cout, *this) << "\n";
  _car.nextWaypoint();
}

//
// Implementation of the MakeTurn class methods
//

MakeTurn::MakeTurn(Timon& car, float turn) :
  Command("MakeTurn", 1.0 + abs(turn) / 25),
  _car(car),
  _turn(turn),
  _initialHeading(0),
  _lastErr(0),
  _inRangeCnt(0)
{
}

MakeTurn::~MakeTurn() {
}

void MakeTurn::doInitialize() {
  _car.print(cout, *this) << "\n";
  _initialHeading = _car.getHeading();
  _lastErr = _turn;
  _inRangeCnt = 0;
}

Command::State MakeTurn::doExecute() {
  float carTurned = _car.getRelativeHeading(_initialHeading);
  float err = _turn - carTurned;
  float deltaErr = _lastErr - err;
  const float P = (0.15f * 10.0f / 360.0f);
  const float D = (0.2f * 10.0f / 360.0f);

  // TODO: Delete once we figure out how to get angle (the
  // statement below makes
  //float percentTimeElapsed = getElapsedTime() / getTimeout();
  //err = (percentTimeElapsed >= 0.5) ? 0.0 : (1.1 - percentTimeElapsed) * _turn;

  float steer = err * P + deltaErr * D;
  float steerMag = abs(steer);
  const float minMag = 0.2f;
  if (steerMag < minMag) {
    steer = (steer < 0) ? -minMag : minMag;
  }

  // Limit to .35 power level
  const float maxMag = 0.30f;
  steer = min(maxMag, max(-maxMag, steer));

  steer = Timon::rangeCheckPower(steer);
  _car.drive(steer, -steer);

  // TODO: NOTE, this implementation does not take into account a minimum
  // power to turn the car (for example, if we get within 10 degrees and
  // drop the power too low, the car may stop turning and never reach
  // the final target).

  _car.print(cout, *this) << "  turned: " << carTurned << "  err: " << err << "\n";

  _lastErr = err;

  // Done if within 3 degrees
  if (abs(err) < 3.0) {
    _inRangeCnt++;
  } else {
    _inRangeCnt = 0;
  }
  return ((_inRangeCnt >= 3) ? Command::NORMAL_END : Command::STILL_RUNNING);
}

void MakeTurn::doEnd(Command::State reason) {
  _car.print(cout, *this) << "\n";
}

//
// Implementation of the DrivePowerTime class methods
//

DrivePowerTime::DrivePowerTime(Timon& car, float powerLeft, float powerRight,
			       float howLong) :
  Command("DrivePowerTime", howLong + 1.0),
  _car(car),
  _powerLeft(powerLeft),
  _powerRight(powerRight),
  _runTime(howLong)
{
}

DrivePowerTime::~DrivePowerTime() {
}

Command::State DrivePowerTime::doExecute() {
  if (getElapsedTime() >= _runTime) {
    return Command::NORMAL_END;
  }

  _car.seekDrive(_powerLeft, _powerRight);
  return Command::STILL_RUNNING;
}

void DrivePowerTime::doEnd(Command::State reason) {
  // If command was to bring car to a stop and the
  if (reason == Command::NORMAL_END) {
    if (_powerLeft == 0 && _powerRight == 0) {
      _car.drive(0, 0);
    }
  }
}
<|endoftext|>


<commit_before>//
//  yas_operation.cpp
//

#include <atomic>
#include <deque>
#include <mutex>
#include <thread>
#include <vector>
#include "yas_operation.h"

using namespace yas;

#pragma mark - operation

class operation::impl : public base::impl {
   public:
    std::atomic<bool> canceled;
    execution_f execution;

    impl(const execution_f &exe) : canceled(false), execution(exe) {
    }
};

operation::operation(const execution_f &exe) : super_class(std::make_unique<impl>(exe)) {
}

operation::operation(std::nullptr_t) : super_class(nullptr) {
}

void operation::cancel() {
    _cancel();
}

bool operation::is_canceled() const {
    return impl_ptr<impl>()->canceled;
}

void operation::_execute() {
    if (auto &exe = impl_ptr<impl>()->execution) {
        if (!is_canceled()) {
            exe(*this);
        }
    }
}

void operation::_cancel() {
    impl_ptr<impl>()->canceled = true;
}

#pragma mark - queue

class operation_queue::impl : public base::impl {
   public:
    impl(const size_t count) : _operations(count) {
    }

    ~impl() {
        cancel_all_operations();
        wait_until_all_operations_are_finished();
    }

    void add_operation(const operation &op, const priority_t priority) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        auto &dq = _operations.at(priority);
        dq.push_back(op);

        _start_next_operation_if_needed();
    }

    void insert_operation_to_top(const operation &op, const priority_t priority) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        auto &dq = _operations.at(priority);
        dq.push_front(op);

        _start_next_operation_if_needed();
    }

    void cancel_operation(const operation &operation) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        for (auto &dq : _operations) {
            for (auto &op : dq) {
                if (operation == op) {
                    op.cancel();
                }
            }
        }

        if (_current_operation) {
            if (_current_operation == operation) {
                _current_operation.cancel();
            }
        }
    }

    void cancel_all_operations() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        for (auto &dq : _operations) {
            for (auto &op : dq) {
                op.cancel();
            }
            dq.clear();
        }

        if (_current_operation) {
            _current_operation.cancel();
        }
    }

    void wait_until_all_operations_are_finished() {
        while (true) {
            std::lock_guard<std::recursive_mutex> lock(_mutex);

            bool op_exists = _current_operation != nullptr;
            if (!op_exists) {
                for (auto &dq : _operations) {
                    if (dq.size() > 0) {
                        op_exists = true;
                    }
                }
            }

            if (op_exists) {
                if (_suspended) {
                    throw "operation_queue is suspended.";
                }
                std::this_thread::yield();
            } else {
                break;
            }
        }
    }

    void suspend() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        _suspended = true;
    }

    void resume() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        if (_suspended) {
            _suspended = false;
            _start_next_operation_if_needed();
        }
    }

   private:
    operation _current_operation = nullptr;
    std::vector<std::deque<operation>> _operations;
    bool _suspended = false;
    mutable std::recursive_mutex _mutex;

    void _start_next_operation_if_needed() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        if (!_current_operation && !_suspended) {
            operation op{nullptr};

            for (auto &dq : _operations) {
                if (!dq.empty()) {
                    op = dq.front();
                    dq.pop_front();
                    break;
                }
            }

            if (op) {
                _current_operation = op;

                auto weak_ope = to_weak(op);
                auto queue = cast<operation_queue>();

                std::thread thread{[weak_ope, queue]() {
                    auto ope = weak_ope.lock();
                    if (ope) {
                        auto &ope_for_queue = static_cast<operation_from_queue &>(ope);
                        ope_for_queue._execute();
                        queue.impl_ptr<impl>()->_operation_did_finish(ope);
                    }
                }};

                thread.detach();
            }
        }
    }

    void _operation_did_finish(const operation &prev_op) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        if (_current_operation == prev_op) {
            _current_operation = nullptr;
        }

        _start_next_operation_if_needed();
    }
};

operation_queue::operation_queue(const size_t count) : super_class(std::make_unique<impl>(count)) {
}

operation_queue::operation_queue(std::nullptr_t) : super_class(nullptr) {
}

void operation_queue::add_operation(const operation &op, const priority_t pr) {
    impl_ptr<impl>()->add_operation(op, pr);
}

void operation_queue::insert_operation_to_top(const operation &op, const priority_t pr) {
    impl_ptr<impl>()->insert_operation_to_top(op, pr);
}

void operation_queue::cancel_operation(const operation &op) {
    impl_ptr<impl>()->cancel_operation(op);
}

void operation_queue::cancel_all_operations() {
    impl_ptr<impl>()->cancel_all_operations();
}

void operation_queue::wait_until_all_operations_are_finished() {
    impl_ptr<impl>()->wait_until_all_operations_are_finished();
}

void operation_queue::suspend() {
    impl_ptr<impl>()->suspend();
}

void operation_queue::resume() {
    impl_ptr<impl>()->resume();
}
<commit_msg>weak_operation<commit_after>//
//  yas_operation.cpp
//

#include <atomic>
#include <deque>
#include <mutex>
#include <thread>
#include <vector>
#include "yas_operation.h"

using namespace yas;

#pragma mark - operation

class operation::impl : public base::impl {
   public:
    std::atomic<bool> canceled;
    execution_f execution;

    impl(const execution_f &exe) : canceled(false), execution(exe) {
    }
};

operation::operation(const execution_f &exe) : super_class(std::make_unique<impl>(exe)) {
}

operation::operation(std::nullptr_t) : super_class(nullptr) {
}

void operation::cancel() {
    _cancel();
}

bool operation::is_canceled() const {
    return impl_ptr<impl>()->canceled;
}

void operation::_execute() {
    if (auto &exe = impl_ptr<impl>()->execution) {
        if (!is_canceled()) {
            exe(*this);
        }
    }
}

void operation::_cancel() {
    impl_ptr<impl>()->canceled = true;
}

#pragma mark - queue

class operation_queue::impl : public base::impl {
   public:
    impl(const size_t count) : _operations(count) {
    }

    ~impl() {
        cancel_all_operations();
        wait_until_all_operations_are_finished();
    }

    void add_operation(const operation &op, const priority_t priority) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        auto &dq = _operations.at(priority);
        dq.push_back(op);

        _start_next_operation_if_needed();
    }

    void insert_operation_to_top(const operation &op, const priority_t priority) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        auto &dq = _operations.at(priority);
        dq.push_front(op);

        _start_next_operation_if_needed();
    }

    void cancel_operation(const operation &operation) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        for (auto &dq : _operations) {
            for (auto &op : dq) {
                if (operation == op) {
                    op.cancel();
                }
            }
        }

        if (_current_operation) {
            if (_current_operation == operation) {
                _current_operation.cancel();
            }
        }
    }

    void cancel_all_operations() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        for (auto &dq : _operations) {
            for (auto &op : dq) {
                op.cancel();
            }
            dq.clear();
        }

        if (_current_operation) {
            _current_operation.cancel();
        }
    }

    void wait_until_all_operations_are_finished() {
        while (true) {
            std::lock_guard<std::recursive_mutex> lock(_mutex);

            bool op_exists = _current_operation != nullptr;
            if (!op_exists) {
                for (auto &dq : _operations) {
                    if (dq.size() > 0) {
                        op_exists = true;
                    }
                }
            }

            if (op_exists) {
                if (_suspended) {
                    throw "operation_queue is suspended.";
                }
                std::this_thread::yield();
            } else {
                break;
            }
        }
    }

    void suspend() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        _suspended = true;
    }

    void resume() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        if (_suspended) {
            _suspended = false;
            _start_next_operation_if_needed();
        }
    }

   private:
    operation _current_operation = nullptr;
    std::vector<std::deque<operation>> _operations;
    bool _suspended = false;
    mutable std::recursive_mutex _mutex;

    void _start_next_operation_if_needed() {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        if (!_current_operation && !_suspended) {
            operation op{nullptr};

            for (auto &dq : _operations) {
                if (!dq.empty()) {
                    op = dq.front();
                    dq.pop_front();
                    break;
                }
            }

            if (op) {
                _current_operation = op;

                auto weak_ope = to_weak(op);
                auto weak_queue = to_weak(cast<operation_queue>());

                std::thread thread{[weak_ope, weak_queue]() {
                    auto ope = weak_ope.lock();
                    if (ope) {
                        auto &ope_for_queue = static_cast<operation_from_queue &>(ope);
                        ope_for_queue._execute();
                        if (auto queue = weak_queue.lock()) {
                            queue.impl_ptr<impl>()->_operation_did_finish(ope);
                        }
                    }
                }};

                thread.detach();
            }
        }
    }

    void _operation_did_finish(const operation &prev_op) {
        std::lock_guard<std::recursive_mutex> lock(_mutex);

        if (_current_operation == prev_op) {
            _current_operation = nullptr;
        }

        _start_next_operation_if_needed();
    }
};

operation_queue::operation_queue(const size_t count) : super_class(std::make_unique<impl>(count)) {
}

operation_queue::operation_queue(std::nullptr_t) : super_class(nullptr) {
}

void operation_queue::add_operation(const operation &op, const priority_t pr) {
    impl_ptr<impl>()->add_operation(op, pr);
}

void operation_queue::insert_operation_to_top(const operation &op, const priority_t pr) {
    impl_ptr<impl>()->insert_operation_to_top(op, pr);
}

void operation_queue::cancel_operation(const operation &op) {
    impl_ptr<impl>()->cancel_operation(op);
}

void operation_queue::cancel_all_operations() {
    impl_ptr<impl>()->cancel_all_operations();
}

void operation_queue::wait_until_all_operations_are_finished() {
    impl_ptr<impl>()->wait_until_all_operations_are_finished();
}

void operation_queue::suspend() {
    impl_ptr<impl>()->suspend();
}

void operation_queue::resume() {
    impl_ptr<impl>()->resume();
}
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include <objbase.h>
#endif
#include <algorithm>

#include "chrome/renderer/render_thread.h"

#include "base/shared_memory.h"
#include "chrome/common/chrome_plugin_lib.h"
#include "chrome/common/ipc_logging.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/notification_service.h"
// TODO(port)
#if defined(OS_WIN)
#include "chrome/plugin/plugin_channel.h"
#else
#include <vector>
#include "base/scoped_handle.h"
#include "chrome/plugin/plugin_channel_base.h"
#include "webkit/glue/weburlrequest.h"
#endif
#include "chrome/renderer/net/render_dns_master.h"
#include "chrome/renderer/render_process.h"
#include "chrome/renderer/render_view.h"
#include "chrome/renderer/user_script_slave.h"
#include "chrome/renderer/visitedlink_slave.h"
#include "webkit/glue/cache_manager.h"

RenderThread* g_render_thread;

static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */;

// V8 needs a 1MB stack size.
static const size_t kStackSize = 1024 * 1024;

//-----------------------------------------------------------------------------
// Methods below are only called on the owner's thread:

RenderThread::RenderThread(const std::wstring& channel_name)
    : Thread("Chrome_RenderThread"),
      owner_loop_(MessageLoop::current()),
      channel_name_(channel_name),
      visited_link_slave_(NULL),
      user_script_slave_(NULL),
      render_dns_master_(NULL),
      in_send_(0) {
  DCHECK(owner_loop_);
  base::Thread::Options options;
  options.stack_size = kStackSize;
  // When we run plugins in process, we actually run them on the render thread,
  // which means that we need to make the render thread pump UI events.
  if (RenderProcess::ShouldLoadPluginsInProcess())
    options.message_loop_type = MessageLoop::TYPE_UI;
  StartWithOptions(options);
}

RenderThread::~RenderThread() {
  Stop();
}

void RenderThread::OnChannelError() {
  owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

bool RenderThread::Send(IPC::Message* msg) {
  in_send_++;
  bool rv = channel_->Send(msg);
  in_send_--;
  return rv;
}

void RenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
  channel_->AddFilter(filter);
}

void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) {
  channel_->RemoveFilter(filter);
}

void RenderThread::Resolve(const char* name, size_t length) {
// TODO(port)
#if defined(OS_WIN)
  return render_dns_master_->Resolve(name, length);
#else
  NOTIMPLEMENTED();
#endif
}

void RenderThread::AddRoute(int32 routing_id,
                            IPC::Channel::Listener* listener) {
  DCHECK(MessageLoop::current() == message_loop());

  // This corresponds to the AddRoute call done in CreateView.
  router_.AddRoute(routing_id, listener);
}

void RenderThread::RemoveRoute(int32 routing_id) {
  DCHECK(MessageLoop::current() == message_loop());

  router_.RemoveRoute(routing_id);
}

void RenderThread::Init() {
  DCHECK(!g_render_thread);
  g_render_thread = this;

  notification_service_.reset(new NotificationService);

  cache_stats_factory_.reset(
      new ScopedRunnableMethodFactory<RenderThread>(this));

  channel_.reset(new IPC::SyncChannel(channel_name_,
      IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true,
      RenderProcess::GetShutDownEvent()));

#if defined(OS_WIN)
  // The renderer thread should wind-up COM.
  CoInitialize(0);
#endif

  visited_link_slave_ = new VisitedLinkSlave();
  user_script_slave_ = new UserScriptSlave();
  render_dns_master_.reset(new RenderDnsMaster());

#ifdef IPC_MESSAGE_LOG_ENABLED
  IPC::Logging::current()->SetIPCSender(this);
#endif
}

void RenderThread::CleanUp() {
  DCHECK(g_render_thread == this);
  g_render_thread = NULL;

  // Need to destruct the SyncChannel to the browser before we go away because
  // it caches a pointer to this thread.
  channel_.reset();

// TODO(port)
#if defined(OS_WIN)
  // Clean up plugin channels before this thread goes away.
  PluginChannelBase::CleanupChannels();
#endif

#ifdef IPC_MESSAGE_LOG_ENABLED
  IPC::Logging::current()->SetIPCSender(NULL);
#endif

  notification_service_.reset();

  delete visited_link_slave_;
  visited_link_slave_ = NULL;

  delete user_script_slave_;
  user_script_slave_ = NULL;

#if defined(OS_WIN)
  CoUninitialize();
#endif
}

void RenderThread::OnUpdateVisitedLinks(base::SharedMemoryHandle table) {
  DCHECK(base::SharedMemory::IsHandleValid(table)) << "Bad table handle";
  visited_link_slave_->Init(table);
}

void RenderThread::OnUpdateUserScripts(
    base::SharedMemoryHandle scripts) {
  DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle";
  user_script_slave_->UpdateScripts(scripts);
}

void RenderThread::OnMessageReceived(const IPC::Message& msg) {
  // NOTE: We could subclass router_ to intercept OnControlMessageReceived, but
  // it seems simpler to just process any control messages that we care about
  // up-front and then send the rest of the messages onto router_.

  if (msg.routing_id() == MSG_ROUTING_CONTROL) {
    IPC_BEGIN_MESSAGE_MAP(RenderThread, msg)
      IPC_MESSAGE_HANDLER(ViewMsg_VisitedLink_NewTable, OnUpdateVisitedLinks)
      IPC_MESSAGE_HANDLER(ViewMsg_SetNextPageID, OnSetNextPageID)
      // TODO(port): removed from render_messages_internal.h;
      // is there a new non-windows message I should add here?
      IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
      IPC_MESSAGE_HANDLER(ViewMsg_SetCacheCapacities, OnSetCacheCapacities)
      IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats,
                          OnGetCacheResourceStats)
      IPC_MESSAGE_HANDLER(ViewMsg_PluginMessage, OnPluginMessage)
      IPC_MESSAGE_HANDLER(ViewMsg_UserScripts_NewScripts,
                          OnUpdateUserScripts)
      // send the rest to the router
      IPC_MESSAGE_UNHANDLED(router_.OnMessageReceived(msg))
    IPC_END_MESSAGE_MAP()
  } else {
    router_.OnMessageReceived(msg);
  }
}

void RenderThread::OnPluginMessage(const FilePath& plugin_path,
                                   const std::vector<uint8>& data) {
  if (!ChromePluginLib::IsInitialized()) {
    return;
  }
  CHECK(ChromePluginLib::IsPluginThread());
  ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path);
  if (chrome_plugin) {
    void *data_ptr = const_cast<void*>(reinterpret_cast<const void*>(&data[0]));
    uint32 data_len = static_cast<uint32>(data.size());
    chrome_plugin->functions().on_message(data_ptr, data_len);
  }
}

void RenderThread::OnSetNextPageID(int32 next_page_id) {
  // This should only be called at process initialization time, so we shouldn't
  // have to worry about thread-safety.
  // TODO(port)
#if !defined(OS_LINUX)
  RenderView::SetNextPageID(next_page_id);
#endif
}

void RenderThread::OnCreateNewView(gfx::NativeViewId parent_hwnd,
                                   ModalDialogEvent modal_dialog_event,
                                   const WebPreferences& webkit_prefs,
                                   int32 view_id) {
  // When bringing in render_view, also bring in webkit's glue and jsbindings.
  base::WaitableEvent* waitable_event = new base::WaitableEvent(
#if defined(OS_WIN)
      modal_dialog_event.event);
#else
      true, false);
#endif

  // TODO(darin): once we have a RenderThread per RenderView, this will need to
  // change to assert that we are not creating more than one view.
  RenderView::Create(
      this, parent_hwnd, waitable_event, MSG_ROUTING_NONE, webkit_prefs,
      new SharedRenderViewCounter(0), view_id);
}

void RenderThread::OnSetCacheCapacities(size_t min_dead_capacity,
                                        size_t max_dead_capacity,
                                        size_t capacity) {
#if defined(OS_WIN)
  CacheManager::SetCapacities(min_dead_capacity, max_dead_capacity, capacity);
#else
  // TODO(port)
  NOTIMPLEMENTED();
#endif
}

void RenderThread::OnGetCacheResourceStats() {
#if defined(OS_WIN)
  CacheManager::ResourceTypeStats stats;
  CacheManager::GetResourceTypeStats(&stats);
  Send(new ViewHostMsg_ResourceTypeStats(stats));
#else
  // TODO(port)
  NOTIMPLEMENTED();
#endif
}

void RenderThread::InformHostOfCacheStats() {
#if defined(OS_WIN)
  CacheManager::UsageStats stats;
  CacheManager::GetUsageStats(&stats);
  Send(new ViewHostMsg_UpdatedCacheStats(stats));
#else
  // TODO(port)
  NOTIMPLEMENTED();
#endif
}

void RenderThread::InformHostOfCacheStatsLater() {
  // Rate limit informing the host of our cache stats.
  if (!cache_stats_factory_->empty())
    return;

  MessageLoop::current()->PostDelayedTask(FROM_HERE,
      cache_stats_factory_->NewRunnableMethod(
          &RenderThread::InformHostOfCacheStats),
      kCacheStatsDelayMS);
}
<commit_msg>Turn on DNS prefetching.<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "build/build_config.h"

#if defined(OS_WIN)
#include <windows.h>
#include <objbase.h>
#endif
#include <algorithm>

#include "chrome/renderer/render_thread.h"

#include "base/shared_memory.h"
#include "chrome/common/chrome_plugin_lib.h"
#include "chrome/common/ipc_logging.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/notification_service.h"
// TODO(port)
#if defined(OS_WIN)
#include "chrome/plugin/plugin_channel.h"
#else
#include <vector>
#include "base/scoped_handle.h"
#include "chrome/plugin/plugin_channel_base.h"
#include "webkit/glue/weburlrequest.h"
#endif
#include "chrome/renderer/net/render_dns_master.h"
#include "chrome/renderer/render_process.h"
#include "chrome/renderer/render_view.h"
#include "chrome/renderer/user_script_slave.h"
#include "chrome/renderer/visitedlink_slave.h"
#include "webkit/glue/cache_manager.h"

RenderThread* g_render_thread;

static const unsigned int kCacheStatsDelayMS = 2000 /* milliseconds */;

// V8 needs a 1MB stack size.
static const size_t kStackSize = 1024 * 1024;

//-----------------------------------------------------------------------------
// Methods below are only called on the owner's thread:

RenderThread::RenderThread(const std::wstring& channel_name)
    : Thread("Chrome_RenderThread"),
      owner_loop_(MessageLoop::current()),
      channel_name_(channel_name),
      visited_link_slave_(NULL),
      user_script_slave_(NULL),
      render_dns_master_(NULL),
      in_send_(0) {
  DCHECK(owner_loop_);
  base::Thread::Options options;
  options.stack_size = kStackSize;
  // When we run plugins in process, we actually run them on the render thread,
  // which means that we need to make the render thread pump UI events.
  if (RenderProcess::ShouldLoadPluginsInProcess())
    options.message_loop_type = MessageLoop::TYPE_UI;
  StartWithOptions(options);
}

RenderThread::~RenderThread() {
  Stop();
}

void RenderThread::OnChannelError() {
  owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}

bool RenderThread::Send(IPC::Message* msg) {
  in_send_++;
  bool rv = channel_->Send(msg);
  in_send_--;
  return rv;
}

void RenderThread::AddFilter(IPC::ChannelProxy::MessageFilter* filter) {
  channel_->AddFilter(filter);
}

void RenderThread::RemoveFilter(IPC::ChannelProxy::MessageFilter* filter) {
  channel_->RemoveFilter(filter);
}

void RenderThread::Resolve(const char* name, size_t length) {
  return render_dns_master_->Resolve(name, length);
}

void RenderThread::AddRoute(int32 routing_id,
                            IPC::Channel::Listener* listener) {
  DCHECK(MessageLoop::current() == message_loop());

  // This corresponds to the AddRoute call done in CreateView.
  router_.AddRoute(routing_id, listener);
}

void RenderThread::RemoveRoute(int32 routing_id) {
  DCHECK(MessageLoop::current() == message_loop());

  router_.RemoveRoute(routing_id);
}

void RenderThread::Init() {
  DCHECK(!g_render_thread);
  g_render_thread = this;

  notification_service_.reset(new NotificationService);

  cache_stats_factory_.reset(
      new ScopedRunnableMethodFactory<RenderThread>(this));

  channel_.reset(new IPC::SyncChannel(channel_name_,
      IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true,
      RenderProcess::GetShutDownEvent()));

#if defined(OS_WIN)
  // The renderer thread should wind-up COM.
  CoInitialize(0);
#endif

  visited_link_slave_ = new VisitedLinkSlave();
  user_script_slave_ = new UserScriptSlave();
  render_dns_master_.reset(new RenderDnsMaster());

#ifdef IPC_MESSAGE_LOG_ENABLED
  IPC::Logging::current()->SetIPCSender(this);
#endif
}

void RenderThread::CleanUp() {
  DCHECK(g_render_thread == this);
  g_render_thread = NULL;

  // Need to destruct the SyncChannel to the browser before we go away because
  // it caches a pointer to this thread.
  channel_.reset();

// TODO(port)
#if defined(OS_WIN)
  // Clean up plugin channels before this thread goes away.
  PluginChannelBase::CleanupChannels();
#endif

#ifdef IPC_MESSAGE_LOG_ENABLED
  IPC::Logging::current()->SetIPCSender(NULL);
#endif

  notification_service_.reset();

  delete visited_link_slave_;
  visited_link_slave_ = NULL;

  delete user_script_slave_;
  user_script_slave_ = NULL;

#if defined(OS_WIN)
  CoUninitialize();
#endif
}

void RenderThread::OnUpdateVisitedLinks(base::SharedMemoryHandle table) {
  DCHECK(base::SharedMemory::IsHandleValid(table)) << "Bad table handle";
  visited_link_slave_->Init(table);
}

void RenderThread::OnUpdateUserScripts(
    base::SharedMemoryHandle scripts) {
  DCHECK(base::SharedMemory::IsHandleValid(scripts)) << "Bad scripts handle";
  user_script_slave_->UpdateScripts(scripts);
}

void RenderThread::OnMessageReceived(const IPC::Message& msg) {
  // NOTE: We could subclass router_ to intercept OnControlMessageReceived, but
  // it seems simpler to just process any control messages that we care about
  // up-front and then send the rest of the messages onto router_.

  if (msg.routing_id() == MSG_ROUTING_CONTROL) {
    IPC_BEGIN_MESSAGE_MAP(RenderThread, msg)
      IPC_MESSAGE_HANDLER(ViewMsg_VisitedLink_NewTable, OnUpdateVisitedLinks)
      IPC_MESSAGE_HANDLER(ViewMsg_SetNextPageID, OnSetNextPageID)
      // TODO(port): removed from render_messages_internal.h;
      // is there a new non-windows message I should add here?
      IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
      IPC_MESSAGE_HANDLER(ViewMsg_SetCacheCapacities, OnSetCacheCapacities)
      IPC_MESSAGE_HANDLER(ViewMsg_GetCacheResourceStats,
                          OnGetCacheResourceStats)
      IPC_MESSAGE_HANDLER(ViewMsg_PluginMessage, OnPluginMessage)
      IPC_MESSAGE_HANDLER(ViewMsg_UserScripts_NewScripts,
                          OnUpdateUserScripts)
      // send the rest to the router
      IPC_MESSAGE_UNHANDLED(router_.OnMessageReceived(msg))
    IPC_END_MESSAGE_MAP()
  } else {
    router_.OnMessageReceived(msg);
  }
}

void RenderThread::OnPluginMessage(const FilePath& plugin_path,
                                   const std::vector<uint8>& data) {
  if (!ChromePluginLib::IsInitialized()) {
    return;
  }
  CHECK(ChromePluginLib::IsPluginThread());
  ChromePluginLib *chrome_plugin = ChromePluginLib::Find(plugin_path);
  if (chrome_plugin) {
    void *data_ptr = const_cast<void*>(reinterpret_cast<const void*>(&data[0]));
    uint32 data_len = static_cast<uint32>(data.size());
    chrome_plugin->functions().on_message(data_ptr, data_len);
  }
}

void RenderThread::OnSetNextPageID(int32 next_page_id) {
  // This should only be called at process initialization time, so we shouldn't
  // have to worry about thread-safety.
  // TODO(port)
#if !defined(OS_LINUX)
  RenderView::SetNextPageID(next_page_id);
#endif
}

void RenderThread::OnCreateNewView(gfx::NativeViewId parent_hwnd,
                                   ModalDialogEvent modal_dialog_event,
                                   const WebPreferences& webkit_prefs,
                                   int32 view_id) {
  // When bringing in render_view, also bring in webkit's glue and jsbindings.
  base::WaitableEvent* waitable_event = new base::WaitableEvent(
#if defined(OS_WIN)
      modal_dialog_event.event);
#else
      true, false);
#endif

  // TODO(darin): once we have a RenderThread per RenderView, this will need to
  // change to assert that we are not creating more than one view.
  RenderView::Create(
      this, parent_hwnd, waitable_event, MSG_ROUTING_NONE, webkit_prefs,
      new SharedRenderViewCounter(0), view_id);
}

void RenderThread::OnSetCacheCapacities(size_t min_dead_capacity,
                                        size_t max_dead_capacity,
                                        size_t capacity) {
#if defined(OS_WIN)
  CacheManager::SetCapacities(min_dead_capacity, max_dead_capacity, capacity);
#else
  // TODO(port)
  NOTIMPLEMENTED();
#endif
}

void RenderThread::OnGetCacheResourceStats() {
#if defined(OS_WIN)
  CacheManager::ResourceTypeStats stats;
  CacheManager::GetResourceTypeStats(&stats);
  Send(new ViewHostMsg_ResourceTypeStats(stats));
#else
  // TODO(port)
  NOTIMPLEMENTED();
#endif
}

void RenderThread::InformHostOfCacheStats() {
#if defined(OS_WIN)
  CacheManager::UsageStats stats;
  CacheManager::GetUsageStats(&stats);
  Send(new ViewHostMsg_UpdatedCacheStats(stats));
#else
  // TODO(port)
  NOTIMPLEMENTED();
#endif
}

void RenderThread::InformHostOfCacheStatsLater() {
  // Rate limit informing the host of our cache stats.
  if (!cache_stats_factory_->empty())
    return;

  MessageLoop::current()->PostDelayedTask(FROM_HERE,
      cache_stats_factory_->NewRunnableMethod(
          &RenderThread::InformHostOfCacheStats),
      kCacheStatsDelayMS);
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/*
 * Copyright 2014 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#ifndef CQL3_ASSIGNMENT_TESTABLE_HH
#define CQL3_ASSIGMNENT_TESTABLE_HH

#include <memory>
#include <vector>

namespace cql3 {

class assignment_testable {
public:
    enum class test_result {
        EXACT_MATCH,
        WEAKLY_ASSIGNABLE,
        NOT_ASSIGNABLE,
    };

    static bool is_assignable(test_result tr) {
        return tr != test_result::NOT_ASSIGNABLE;
    }

    static bool is_exact_match(test_result tr) {
        return tr != test_result::EXACT_MATCH;
    }

    // Test all elements of toTest for assignment. If all are exact match, return exact match. If any is not assignable,
    // return not assignable. Otherwise, return weakly assignable.
    static test_result test_all(sstring keyspace, const column_specification& receiver, const std::vector<::shared_ptr<assignment_testable>>& to_test) {
        test_result res = test_result::EXACT_MATCH;
        for (auto&& rt : to_test) {
            if (rt == nullptr) {
                res = test_result::WEAKLY_ASSIGNABLE;
                continue;
            }

            test_result t = rt->test_assignment(keyspace, receiver);
            if (t == test_result::NOT_ASSIGNABLE) {
                return test_result::NOT_ASSIGNABLE;
            }
            if (t == test_result::WEAKLY_ASSIGNABLE) {
                res = test_result::WEAKLY_ASSIGNABLE;
            }
        }
        return res;
    }

    /**
     * @return whether this object can be assigned to the provided receiver. We distinguish
     * between 3 values:
     *   - EXACT_MATCH if this object is exactly of the type expected by the receiver
     *   - WEAKLY_ASSIGNABLE if this object is not exactly the expected type but is assignable nonetheless
     *   - NOT_ASSIGNABLE if it's not assignable
     * Most caller should just call the isAssignable() method on the result, though functions have a use for
     * testing "strong" equality to decide the most precise overload to pick when multiple could match.
     */
    virtual test_result test_assignment(sstring keyspace, const column_specification& receiver) = 0;
};

}

#endif
<commit_msg>cql3: Fix assignment_testable.hh include guard<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */

/*
 * Copyright 2014 Cloudius Systems
 *
 * Modified by Cloudius Systems
 */

#ifndef CQL3_ASSIGNMENT_TESTABLE_HH
#define CQL3_ASSIGNMENT_TESTABLE_HH

#include <memory>
#include <vector>

namespace cql3 {

class assignment_testable {
public:
    enum class test_result {
        EXACT_MATCH,
        WEAKLY_ASSIGNABLE,
        NOT_ASSIGNABLE,
    };

    static bool is_assignable(test_result tr) {
        return tr != test_result::NOT_ASSIGNABLE;
    }

    static bool is_exact_match(test_result tr) {
        return tr != test_result::EXACT_MATCH;
    }

    // Test all elements of toTest for assignment. If all are exact match, return exact match. If any is not assignable,
    // return not assignable. Otherwise, return weakly assignable.
    static test_result test_all(sstring keyspace, const column_specification& receiver, const std::vector<::shared_ptr<assignment_testable>>& to_test) {
        test_result res = test_result::EXACT_MATCH;
        for (auto&& rt : to_test) {
            if (rt == nullptr) {
                res = test_result::WEAKLY_ASSIGNABLE;
                continue;
            }

            test_result t = rt->test_assignment(keyspace, receiver);
            if (t == test_result::NOT_ASSIGNABLE) {
                return test_result::NOT_ASSIGNABLE;
            }
            if (t == test_result::WEAKLY_ASSIGNABLE) {
                res = test_result::WEAKLY_ASSIGNABLE;
            }
        }
        return res;
    }

    /**
     * @return whether this object can be assigned to the provided receiver. We distinguish
     * between 3 values:
     *   - EXACT_MATCH if this object is exactly of the type expected by the receiver
     *   - WEAKLY_ASSIGNABLE if this object is not exactly the expected type but is assignable nonetheless
     *   - NOT_ASSIGNABLE if it's not assignable
     * Most caller should just call the isAssignable() method on the result, though functions have a use for
     * testing "strong" equality to decide the most precise overload to pick when multiple could match.
     */
    virtual test_result test_assignment(sstring keyspace, const column_specification& receiver) = 0;
};

}

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"
#include "base/path_service.h"
#include "base/platform_thread.h"
#include "base/string_util.h"
#include "base/test/test_file_util.h"
#include "base/time.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"

using base::TimeDelta;
using base::TimeTicks;

namespace {

class StartupTest : public UITest {
 public:
  StartupTest() {
    show_window_ = true;
    pages_ = "about:blank";
  }
  void SetUp() {}
  void TearDown() {}

  // Load a file on startup rather than about:blank.  This tests a longer
  // startup path, including resource loading and the loading of gears.dll.
  void SetUpWithFileURL() {
    FilePath file_url;
    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &file_url));
    file_url = file_url.AppendASCII("simple.html");
    ASSERT_TRUE(file_util::PathExists(file_url));
    launch_arguments_.AppendLooseValue(file_url.ToWStringHack());

    pages_ = WideToUTF8(file_url.ToWStringHack());
  }

  // Use the given profile in the test data extensions/profiles dir.  This tests
  // startup with extensions installed.
  void SetUpWithExtensionsProfile(const char* profile) {
    FilePath data_dir;
    PathService::Get(chrome::DIR_TEST_DATA, &data_dir);
    data_dir = data_dir.AppendASCII("extensions").AppendASCII("profiles").
        AppendASCII(profile);
    set_template_user_data(data_dir);
  }

  void RunStartupTest(const char* graph, const char* trace,
      bool test_cold, bool important, UITest::ProfileType profile_type) {
    profile_type_ = profile_type;

    // Sets the profile data for the run.  For now, this is only used for
    // the non-default themes test.
    if (profile_type != UITest::DEFAULT_THEME) {
      set_template_user_data(UITest::ComputeTypicalUserDataSource(
          profile_type));
    }

    const int kNumCyclesMax = 20;
    int numCycles = kNumCyclesMax;
// It's ok for unit test code to use getenv(), isn't it?
#if defined(OS_WIN)
#pragma warning( disable : 4996 )
#endif
    const char* numCyclesEnv = getenv("STARTUP_TESTS_NUMCYCLES");
    if (numCyclesEnv && StringToInt(numCyclesEnv, &numCycles))
      LOG(INFO) << "STARTUP_TESTS_NUMCYCLES set in environment, "
                << "so setting numCycles to " << numCycles;

    TimeDelta timings[kNumCyclesMax];
    for (int i = 0; i < numCycles; ++i) {
      if (test_cold) {
        FilePath dir_app;
        ASSERT_TRUE(PathService::Get(chrome::DIR_APP, &dir_app));

        FilePath chrome_exe(dir_app.Append(
            FilePath::FromWStringHack(chrome::kBrowserProcessExecutablePath)));
        ASSERT_TRUE(EvictFileFromSystemCacheWrapper(chrome_exe));
#if defined(OS_WIN)
        // chrome.dll is windows specific.
        FilePath chrome_dll(dir_app.Append(FILE_PATH_LITERAL("chrome.dll")));
        ASSERT_TRUE(EvictFileFromSystemCacheWrapper(chrome_dll));
#endif

#if defined(OS_WIN)
        // TODO(port): Re-enable once gears is working on mac/linux.
        FilePath gears_dll;
        ASSERT_TRUE(PathService::Get(chrome::FILE_GEARS_PLUGIN, &gears_dll));
        ASSERT_TRUE(EvictFileFromSystemCacheWrapper(gears_dll));
#else
        NOTIMPLEMENTED() << "gears not enabled yet";
#endif
      }
      UITest::SetUp();
      TimeTicks end_time = TimeTicks::Now();
      timings[i] = end_time - browser_launch_time_;
      // TODO(beng): Can't shut down so quickly. Figure out why, and fix. If we
      // do, we crash.
      PlatformThread::Sleep(50);
      UITest::TearDown();

      if (i == 0) {
        // Re-use the profile data after first run so that the noise from
        // creating databases doesn't impact all the runs.
        clear_profile_ = false;
        // Clear template_user_data_ so we don't try to copy it over each time
        // through.
        set_template_user_data(FilePath());
      }
    }

    std::string times;
    for (int i = 0; i < numCycles; ++i)
      StringAppendF(&times, "%.2f,", timings[i].InMillisecondsF());
    PrintResultList(graph, "", trace, times, "ms", important);
  }

 protected:
  std::string pages_;
};

class StartupReferenceTest : public StartupTest {
 public:
  // override the browser directory that is used by UITest::SetUp to cause it
  // to use the reference build instead.
  void SetUp() {
    FilePath dir;
    PathService::Get(chrome::DIR_TEST_TOOLS, &dir);
    dir = dir.AppendASCII("reference_build");
#if defined(OS_WIN)
    dir = dir.AppendASCII("chrome");
#elif defined(OS_LINUX)
    dir = dir.AppendASCII("chrome_linux");
#elif defined(OS_MACOSX)
    dir = dir.AppendASCII("chrome_mac");
#endif
    browser_directory_ = dir;
  }
};

TEST_F(StartupTest, Perf) {
  RunStartupTest("warm", "t", false /* not cold */, true /* important */,
                 UITest::DEFAULT_THEME);
}

// TODO(port): We need a mac reference build checked in for this.
TEST_F(StartupReferenceTest, Perf) {
  RunStartupTest("warm", "t_ref", false /* not cold */,
                 true /* important */, UITest::DEFAULT_THEME);
}

// TODO(mpcomplete): Should we have reference timings for all these?

TEST_F(StartupTest, PerfCold) {
  RunStartupTest("cold", "t", true /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

#if defined(OS_MACOSX)
// TODO(mpcomplete): running these tests on a mac builder results in leaked
// chrome processes, causing the build slave to hang.
// http://code.google.com/p/chromium/issues/detail?id=22287
#else
TEST_F(StartupTest, PerfExtensionEmpty) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("empty");
  RunStartupTest("warm", "t", false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionToolstrips1) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("toolstrips1");
  RunStartupTest("warm", "extension_toolstrip1",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionToolstrips50) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("toolstrips50");
  RunStartupTest("warm", "extension_toolstrip50",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionContentScript1) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("content_scripts1");
  RunStartupTest("warm", "extension_content_scripts1",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionContentScript50) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("content_scripts50");
  RunStartupTest("warm", "extension_content_scripts50",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}
#endif

#if defined(OS_WIN)
// TODO(port): Enable gears tests on linux/mac once gears is working.
TEST_F(StartupTest, PerfGears) {
  SetUpWithFileURL();
  RunStartupTest("warm", "gears", false /* not cold */,
                 false /* not important */, UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfColdGears) {
  SetUpWithFileURL();
  RunStartupTest("cold", "gears", true /* cold */,
                 false /* not important */, UITest::DEFAULT_THEME);
}
#endif

TEST_F(StartupTest, PerfColdComplexTheme) {
  RunStartupTest("warm", "t-theme", false /* warm */,
                 false /* not important */, UITest::COMPLEX_THEME);
}

#if defined(OS_LINUX)
TEST_F(StartupTest, PerfColdGtkTheme) {
  RunStartupTest("warm", "gtk-theme", false /* warm */,
                 false /* not important */, UITest::NATIVE_THEME);
}

TEST_F(StartupTest, PrefColdNativeFrame) {
  RunStartupTest("warm", "custom-frame", false /* warm */,
                 false /* not important */, UITest::CUSTOM_FRAME);
}

TEST_F(StartupTest, PerfColdNativeFrameGtkTheme) {
  RunStartupTest("warm", "custom-frame-gtk-theme", false /* warm */,
                 false /* not important */, UITest::CUSTOM_FRAME_NATIVE_THEME);
}
#endif

}  // namespace
<commit_msg>reenable toolstrips for startup tests<commit_after>// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"
#include "base/path_service.h"
#include "base/platform_thread.h"
#include "base/string_util.h"
#include "base/test/test_file_util.h"
#include "base/time.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"

using base::TimeDelta;
using base::TimeTicks;

namespace {

class StartupTest : public UITest {
 public:
  StartupTest() {
    show_window_ = true;
    pages_ = "about:blank";
  }
  void SetUp() {}
  void TearDown() {}

  // Load a file on startup rather than about:blank.  This tests a longer
  // startup path, including resource loading and the loading of gears.dll.
  void SetUpWithFileURL() {
    FilePath file_url;
    ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &file_url));
    file_url = file_url.AppendASCII("simple.html");
    ASSERT_TRUE(file_util::PathExists(file_url));
    launch_arguments_.AppendLooseValue(file_url.ToWStringHack());

    pages_ = WideToUTF8(file_url.ToWStringHack());
  }

  // Use the given profile in the test data extensions/profiles dir.  This tests
  // startup with extensions installed.
  void SetUpWithExtensionsProfile(const char* profile) {
    FilePath data_dir;
    PathService::Get(chrome::DIR_TEST_DATA, &data_dir);
    data_dir = data_dir.AppendASCII("extensions").AppendASCII("profiles").
        AppendASCII(profile);
    set_template_user_data(data_dir);

    // For now, these tests still depend on toolstrips.
    launch_arguments_.AppendSwitch(switches::kEnableExtensionToolstrips);
  }

  void RunStartupTest(const char* graph, const char* trace,
      bool test_cold, bool important, UITest::ProfileType profile_type) {
    profile_type_ = profile_type;

    // Sets the profile data for the run.  For now, this is only used for
    // the non-default themes test.
    if (profile_type != UITest::DEFAULT_THEME) {
      set_template_user_data(UITest::ComputeTypicalUserDataSource(
          profile_type));
    }

    const int kNumCyclesMax = 20;
    int numCycles = kNumCyclesMax;
// It's ok for unit test code to use getenv(), isn't it?
#if defined(OS_WIN)
#pragma warning( disable : 4996 )
#endif
    const char* numCyclesEnv = getenv("STARTUP_TESTS_NUMCYCLES");
    if (numCyclesEnv && StringToInt(numCyclesEnv, &numCycles))
      LOG(INFO) << "STARTUP_TESTS_NUMCYCLES set in environment, "
                << "so setting numCycles to " << numCycles;

    TimeDelta timings[kNumCyclesMax];
    for (int i = 0; i < numCycles; ++i) {
      if (test_cold) {
        FilePath dir_app;
        ASSERT_TRUE(PathService::Get(chrome::DIR_APP, &dir_app));

        FilePath chrome_exe(dir_app.Append(
            FilePath::FromWStringHack(chrome::kBrowserProcessExecutablePath)));
        ASSERT_TRUE(EvictFileFromSystemCacheWrapper(chrome_exe));
#if defined(OS_WIN)
        // chrome.dll is windows specific.
        FilePath chrome_dll(dir_app.Append(FILE_PATH_LITERAL("chrome.dll")));
        ASSERT_TRUE(EvictFileFromSystemCacheWrapper(chrome_dll));
#endif

#if defined(OS_WIN)
        // TODO(port): Re-enable once gears is working on mac/linux.
        FilePath gears_dll;
        ASSERT_TRUE(PathService::Get(chrome::FILE_GEARS_PLUGIN, &gears_dll));
        ASSERT_TRUE(EvictFileFromSystemCacheWrapper(gears_dll));
#else
        NOTIMPLEMENTED() << "gears not enabled yet";
#endif
      }
      UITest::SetUp();
      TimeTicks end_time = TimeTicks::Now();
      timings[i] = end_time - browser_launch_time_;
      // TODO(beng): Can't shut down so quickly. Figure out why, and fix. If we
      // do, we crash.
      PlatformThread::Sleep(50);
      UITest::TearDown();

      if (i == 0) {
        // Re-use the profile data after first run so that the noise from
        // creating databases doesn't impact all the runs.
        clear_profile_ = false;
        // Clear template_user_data_ so we don't try to copy it over each time
        // through.
        set_template_user_data(FilePath());
      }
    }

    std::string times;
    for (int i = 0; i < numCycles; ++i)
      StringAppendF(&times, "%.2f,", timings[i].InMillisecondsF());
    PrintResultList(graph, "", trace, times, "ms", important);
  }

 protected:
  std::string pages_;
};

class StartupReferenceTest : public StartupTest {
 public:
  // override the browser directory that is used by UITest::SetUp to cause it
  // to use the reference build instead.
  void SetUp() {
    FilePath dir;
    PathService::Get(chrome::DIR_TEST_TOOLS, &dir);
    dir = dir.AppendASCII("reference_build");
#if defined(OS_WIN)
    dir = dir.AppendASCII("chrome");
#elif defined(OS_LINUX)
    dir = dir.AppendASCII("chrome_linux");
#elif defined(OS_MACOSX)
    dir = dir.AppendASCII("chrome_mac");
#endif
    browser_directory_ = dir;
  }
};

TEST_F(StartupTest, Perf) {
  RunStartupTest("warm", "t", false /* not cold */, true /* important */,
                 UITest::DEFAULT_THEME);
}

// TODO(port): We need a mac reference build checked in for this.
TEST_F(StartupReferenceTest, Perf) {
  RunStartupTest("warm", "t_ref", false /* not cold */,
                 true /* important */, UITest::DEFAULT_THEME);
}

// TODO(mpcomplete): Should we have reference timings for all these?

TEST_F(StartupTest, PerfCold) {
  RunStartupTest("cold", "t", true /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

#if defined(OS_MACOSX)
// TODO(mpcomplete): running these tests on a mac builder results in leaked
// chrome processes, causing the build slave to hang.
// http://code.google.com/p/chromium/issues/detail?id=22287
#else
TEST_F(StartupTest, PerfExtensionEmpty) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("empty");
  RunStartupTest("warm", "t", false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionToolstrips1) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("toolstrips1");
  RunStartupTest("warm", "extension_toolstrip1",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionToolstrips50) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("toolstrips50");
  RunStartupTest("warm", "extension_toolstrip50",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionContentScript1) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("content_scripts1");
  RunStartupTest("warm", "extension_content_scripts1",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfExtensionContentScript50) {
  SetUpWithFileURL();
  SetUpWithExtensionsProfile("content_scripts50");
  RunStartupTest("warm", "extension_content_scripts50",
                 false /* cold */, false /* not important */,
                 UITest::DEFAULT_THEME);
}
#endif

#if defined(OS_WIN)
// TODO(port): Enable gears tests on linux/mac once gears is working.
TEST_F(StartupTest, PerfGears) {
  SetUpWithFileURL();
  RunStartupTest("warm", "gears", false /* not cold */,
                 false /* not important */, UITest::DEFAULT_THEME);
}

TEST_F(StartupTest, PerfColdGears) {
  SetUpWithFileURL();
  RunStartupTest("cold", "gears", true /* cold */,
                 false /* not important */, UITest::DEFAULT_THEME);
}
#endif

TEST_F(StartupTest, PerfColdComplexTheme) {
  RunStartupTest("warm", "t-theme", false /* warm */,
                 false /* not important */, UITest::COMPLEX_THEME);
}

#if defined(OS_LINUX)
TEST_F(StartupTest, PerfColdGtkTheme) {
  RunStartupTest("warm", "gtk-theme", false /* warm */,
                 false /* not important */, UITest::NATIVE_THEME);
}

TEST_F(StartupTest, PrefColdNativeFrame) {
  RunStartupTest("warm", "custom-frame", false /* warm */,
                 false /* not important */, UITest::CUSTOM_FRAME);
}

TEST_F(StartupTest, PerfColdNativeFrameGtkTheme) {
  RunStartupTest("warm", "custom-frame-gtk-theme", false /* warm */,
                 false /* not important */, UITest::CUSTOM_FRAME_NATIVE_THEME);
}
#endif

}  // namespace
<|endoftext|>