<commit_before>// Copyright (c) 2009 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 <atlbase.h>

#include "base/logging.h"
#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
#include "chrome_frame/crash_reporting/veh_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

ACTION_P(StackTraceDump, s) {
  memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0]));
  return s->count_;
}
namespace {
class MockApi : public Win32VEHTraits, public ModuleOfInterest {
 public:
  MockApi() {
    Win32VEHTraits::InitializeIgnoredBlocks();
  }

  MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*));
  MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*());
  MOCK_METHOD4(RtlCaptureStackBackTrace, WORD(DWORD FramesToSkip,
      DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash));

  // Helpers
  void SetSEH(const SEHChain& sehchain) {
    EXPECT_CALL(*this, RtlpGetExceptionList())
        .Times(testing::AnyNumber())
        .WillRepeatedly(testing::Return(sehchain.chain_));
  }

  void SetStack(const StackHelper& s) {
    EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _))
        .Times(testing::AnyNumber())
        .WillRepeatedly(StackTraceDump(&s));
  }

  enum {max_back_trace = 15};
};
};  // namespace

typedef VectoredHandlerT<MockApi> VectoredHandlerMock;
TEST(ChromeFrame, ExceptionReport) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain have_seh(s - 0x1000, s + 0x1000, e + 0x7127, NULL);
  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  StackHelper not_on_stack(s - 0x11283, s - 0x278361, e + 1231, NULL);

  char* our_code = s + 0x1111;
  char* not_our_code = s - 0x5555;
  char* not_our_code2 = e + 0x5555;

  // Exception in our code, but we have SEH filter => no dump.
  api.SetSEH(have_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
            veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
  // from its clients", shall not be caught since it's a warning only.
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(RPC_E_DISCONNECTED, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, we do not have SEH and we are not in stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, no SEH, but we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO,
                                 not_our_code2)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  char* ignore_address = reinterpret_cast<char*>(GetProcAddress(
      GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10;
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are not on the stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);
}

MATCHER_P(ExceptionCodeIs, code, "") {
  return (arg->ExceptionRecord->ExceptionCode == code);
}

void OverflowStack() {
  char tmp[1024 * 2048];
  ZeroMemory(tmp, sizeof(tmp));
}

DWORD WINAPI CrashingThread(PVOID tmp) {
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  // This will cause STATUS_ACCESS_VIOLATION
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  return 0;
}

static VectoredHandlerMock* g_mock_veh = NULL;
static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) {
  return g_mock_veh->Handler(exptrs);
}

TEST(ChromeFrame, TrickyStackOverflow) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  api.SetSEH(no_seh);
  api.SetStack(on_stack);

  EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1);

  g_mock_veh = &veh;
  void* id = ::AddVectoredExceptionHandler(FALSE, VEH);

  DWORD tid;
  HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid);
  ::WaitForSingleObject(h, INFINITE);
  ::CloseHandle(h);

  EXPECT_EQ(2, veh.get_exceptions_seen());
  ::RemoveVectoredExceptionHandler(id);
  g_mock_veh = NULL;
}
<commit_msg>Committing for stoyan since we need this now...<commit_after>// Copyright (c) 2009 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 <atlbase.h>

#include "base/logging.h"
#include "chrome_frame/crash_reporting/vectored_handler-impl.h"
#include "chrome_frame/crash_reporting/veh_test.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;

ACTION_P(StackTraceDump, s) {
  memcpy(arg2, s->stack_, s->count_ * sizeof(s->stack_[0]));
  return s->count_;
}
namespace {
class MockApi : public Win32VEHTraits, public ModuleOfInterest {
 public:
  MockApi() {
    Win32VEHTraits::InitializeIgnoredBlocks();
  }

  MOCK_METHOD1(WriteDump, void(const EXCEPTION_POINTERS*));
  MOCK_METHOD0(RtlpGetExceptionList, const EXCEPTION_REGISTRATION_RECORD*());
  MOCK_METHOD4(RtlCaptureStackBackTrace, WORD(DWORD FramesToSkip,
      DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash));

  // Helpers
  void SetSEH(const SEHChain& sehchain) {
    EXPECT_CALL(*this, RtlpGetExceptionList())
        .Times(testing::AnyNumber())
        .WillRepeatedly(testing::Return(sehchain.chain_));
  }

  void SetStack(const StackHelper& s) {
    EXPECT_CALL(*this, RtlCaptureStackBackTrace(_, _, _, _))
        .Times(testing::AnyNumber())
        .WillRepeatedly(StackTraceDump(&s));
  }

  enum {max_back_trace = 15};
};
};  // namespace

typedef VectoredHandlerT<MockApi> VectoredHandlerMock;
TEST(ChromeFrame, ExceptionReport) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain have_seh(s - 0x1000, s + 0x1000, e + 0x7127, NULL);
  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  StackHelper not_on_stack(s - 0x11283, s - 0x278361, e + 1231, NULL);

  char* our_code = s + 0x1111;
  char* not_our_code = s - 0x5555;
  char* not_our_code2 = e + 0x5555;

  // Exception in our code, but we have SEH filter => no dump.
  api.SetSEH(have_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
            veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected
  // from its clients", shall not be caught since it's a warning only.
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(RPC_E_DISCONNECTED, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, we do not have SEH and we are not in stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, not in our code, no SEH, but we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_INTEGER_DIVIDE_BY_ZERO,
                                 not_our_code2)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in our code, no SEH, not on stack (assume FPO screwed us)
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(1);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_PRIVILEGED_INSTRUCTION, our_code)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are on the stack.
  api.SetSEH(no_seh);
  api.SetStack(on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  char* ignore_address = reinterpret_cast<char*>(GetProcAddress(
      GetModuleHandleA("kernel32.dll"), "IsBadStringPtrA")) + 10;
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);

  // Exception, in IsBadStringPtrA, we are not on the stack.
  api.SetSEH(no_seh);
  api.SetStack(not_on_stack);
  EXPECT_CALL(api, WriteDump(_)).Times(0);
  EXPECT_EQ(ExceptionContinueSearch,
      veh.Handler(&ExceptionInfo(STATUS_ACCESS_VIOLATION, ignore_address)));
  testing::Mock::VerifyAndClearExpectations(&api);
}

MATCHER_P(ExceptionCodeIs, code, "") {
  return (arg->ExceptionRecord->ExceptionCode == code);
}

DECLSPEC_NOINLINE static void OverflowStack() {
  char tmp[1024 * 2048] = {0};
  GetSystemInfo(reinterpret_cast<SYSTEM_INFO*>(&tmp));
}

DWORD WINAPI CrashingThread(PVOID tmp) {
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  // This will cause STATUS_ACCESS_VIOLATION
  __try {
    OverflowStack();
  } __except(EXCEPTION_EXECUTE_HANDLER) {

  }

  return 0;
}

static VectoredHandlerMock* g_mock_veh = NULL;
static LONG WINAPI VEH(EXCEPTION_POINTERS* exptrs) {
  return g_mock_veh->Handler(exptrs);
}

TEST(ChromeFrame, TrickyStackOverflow) {
  MockApi api;
  VectoredHandlerMock veh(&api);

  // Start address of our module.
  char* s = reinterpret_cast<char*>(0x30000000);
  char *e = s + 0x10000;
  api.SetModule(s, e);

  SEHChain no_seh(s - 0x1000, e + 0x7127, NULL);
  StackHelper on_stack(s - 0x11283, s - 0x278361, s + 0x9171, e + 1231, NULL);
  api.SetSEH(no_seh);
  api.SetStack(on_stack);

  EXPECT_CALL(api, WriteDump(ExceptionCodeIs(STATUS_STACK_OVERFLOW))).Times(1);

  g_mock_veh = &veh;
  void* id = ::AddVectoredExceptionHandler(FALSE, VEH);

  DWORD tid;
  HANDLE h = ::CreateThread(0, 0, CrashingThread, 0, 0, &tid);
  ::WaitForSingleObject(h, INFINITE);
  ::CloseHandle(h);

  EXPECT_EQ(2, veh.get_exceptions_seen());
  ::RemoveVectoredExceptionHandler(id);
  g_mock_veh = NULL;
}
<|endoftext|>


<commit_before>#include"all_defines.hpp"

#include"types.hpp"
#include"workers.hpp"
#include"processes.hpp"
#include"mutexes.hpp"
#include"lockeds.hpp"
#include"symbols.hpp"

#include<boost/noncopyable.hpp>

#include<iostream>

/*-----------------------------------------------------------------------------
AllWorkers
-----------------------------------------------------------------------------*/

/*
 * Registration
 */

void AllWorkers::register_process(Process* P) {
	AppLock l(U_mtx);
	U.push_back(P);
}

void AllWorkers::register_worker(Worker* W) {
	AppLock l(general_mtx);
	Ws.push_back(W);
	total_workers++;
}

void AllWorkers::unregister_worker(Worker* W) {
	AppLock lg(general_mtx);
	size_t l = Ws.size();
	for(size_t i = 0; i < l; ++i) {
		if(Ws[i] == W) {
			Ws[i] = Ws[l - 1];
			Ws.resize(l - 1);
			--total_workers;
			/*check if this has changed any of the
			waiting states
			*/
			if(soft_stop_condition) {
				if(soft_stop_waiting == total_workers) {
					soft_stop_waiting = 0;
					soft_stop_cv.broadcast();
				}
			}
			if(workqueue_waiting > 0) {
				if(workqueue_waiting == total_workers) {
					workqueue_waiting = 0;
					exit_condition = 1;
					workqueue_cv.broadcast();
				}
			}
			return;
		}
	}
}

/*
 * Soft-stop
 */

void AllWorkers::soft_stop_raise(void) {
	AppLock l(general_mtx);
	soft_stop_condition = 1;
	if(workqueue_waiting != 0) {
		workqueue_cv.broadcast();
	}
	soft_stop_waiting++;
	soft_stop_cv.wait(l);
}
void AllWorkers::soft_stop_lower(void) {
	AppLock l(general_mtx);
	soft_stop_condition = 0;
	soft_stop_cv.broadcast();
}

void AllWorkers::soft_stop_check(AppLock& l) {
	while(soft_stop_condition) {
		soft_stop_waiting++;
		if(soft_stop_waiting == total_workers) {
			soft_stop_waiting = 0;
			soft_stop_cv.broadcast();
		} else {
			soft_stop_cv.wait(l);
		}
	}
}

/*
 * Workqueue
 */

void AllWorkers::workqueue_push(Process* R) {
	AppLock l(general_mtx);
	bool sig = workqueue.empty();
	workqueue.push(R);
	if(sig) workqueue_cv.broadcast();
}
void AllWorkers::workqueue_push_and_pop(Process*& R) {
	AppLock l(general_mtx);
	soft_stop_check(l);
	/*if workqueue is empty, we'd end up popping what we
	would have pushed anyway, so just short-circuit it
	*/
	if(workqueue.empty()) return;
	workqueue.push(R);
	R = workqueue.front();
	workqueue.pop();
}
bool AllWorkers::workqueue_pop(Process*& R) {
	AppLock l(general_mtx);
start:
	soft_stop_check(l);
	if(!workqueue.empty()) {
		R = workqueue.front();
		workqueue.pop();
		return 1;
	}
retry:
	workqueue_waiting++;
	if(workqueue_waiting == total_workers) {
		workqueue_waiting = 0;
		exit_condition = 1;
		return 0;
	}
	workqueue_cv.wait(l);
	if(exit_condition) return 0;
	--workqueue_waiting;
	/*if we exited due to a soft-stop, bar*/
	if(soft_stop_condition) goto start;
	if(workqueue.empty()) goto retry;
	R = workqueue.front();
	workqueue.pop();
	return 1;
}

/*-----------------------------------------------------------------------------
Worker
-----------------------------------------------------------------------------*/

void Worker::operator()(void) {
	try {
		try {
			parent->register_worker(this);
			work();
			parent->unregister_worker(this);
		} catch(std::exception& e) {
			std::cerr << "Unhandled exception:" << std::endl;
			std::cerr << e.what() << std::endl;
			std::cerr << "aborting a worker thread..."
				<< std::endl;
			std::cerr.flush();
			parent->unregister_worker(this);
			return;
		}
	} catch(...) {
		std::cerr << "Unknown exception!" << std::endl;
		std::cerr << "aborting a worker thread..."
			<< std::endl;
		std::cerr.flush();
		parent->unregister_worker(this);
		return;
	}
}

/*
 * Helper RAII classes
 */

class MarkingTraverser : public HeapTraverser {
private:
	std::set<Process*>* pgray_set;
public:
	explicit MarkingTraverser(std::set<Process*>& ngray_set)
		: pgray_set(&ngray_set) { }
	virtual void traverse(Generic* gp) {
		HlPid* pp = dynamic_cast<HlPid*>(gp);
		if(pp && pp->process->waiting_and_not_black()){
			pgray_set->insert(pp->process);
		}
	}
};

/*Holds the currently-running process.  If something throws
while we're running that process, just kill the process and
drop its references.
*/
class RunningProcessRef : boost::noncopyable {
public:
	Process* p;
	RunningProcessRef(void) : p(0) { }
	RunningProcessRef& operator=(Process* np) { p = np; }
	~RunningProcessRef() {
		/*DON'T delete p, just kill it*/
		if(p) p->atomic_kill();
	}
	operator Process*&(void) { return p; }

	Process* operator->(void) { return p; }
	Process* operator->(void) const { return p; }
	Process& operator*(void) { return *p; }
	Process& operator*(void) const { return *p; }
};

/*Causes a soft-stop on other worker threads
*/
class SoftStop : boost::noncopyable {
	AllWorkers* parent;
	#ifndef single_threaded
		bool single_threaded_save;
	#endif
public:
	explicit SoftStop(AllWorkers* nparent)
		: parent(nparent) {
		parent->soft_stop_raise();
		#ifndef single_threaded
			single_threaded_save = single_threaded;
			single_threaded = 1;
		#endif
	}
	~SoftStop() {
		#ifndef single_threaded
			single_threaded = single_threaded_save;
		#endif
		parent->soft_stop_lower();
	}
};

class AnesthesizeProcess : boost::noncopyable {
	AllWorkers* parent;
	Process* P;
public:
	bool succeeded;
	AnesthesizeProcess(Process* nP, AllWorkers* nparent)
		: parent(nparent),
		  P(nP),
		  succeeded(nP->anesthesize()) { }
	~AnesthesizeProcess() {
		if(succeeded) {
			if(P->unanesthesize()) {
				/*Process::unanesthesize should have
				set status to process_running
				*/
				parent->workqueue_push(P);
			}
		}
	}
};

/*
 * Marks all PID's of a process
 */
void Worker::mark_process(Process* P) {
	MarkingTraverser mt(gray_set);
	P->heap().traverse_objects(&mt);

	/*scan the mailbox*/
	ValueHolderRef tmp;
	LockedValueHolderRef& mailbox = P->mailbox();
	mailbox.swap(tmp);
	if(!tmp.empty()) {
		/*shouldn't throw: traverse_objects doesn't
		throw, and MarkingTraverser doesn't either
		*/
		tmp->traverse_objects(&mt);
		/*put back the contents into the mailbox*/
		mailbox.swap(tmp);
	}

	/*another process might have sent new messages
	while we were marking - so tmp might not be
	empty.  We just push tmp one-by-one onto the
	mailbox.
	*/
	/*
	We don't have to rescan the new messages - we
	only mark during a process-level GC, and
	messages that are sent while a GC is going on
	cannot contain references to white processes,
	because only black processes are actually
	allowed to run (and by extension, send
	messages).
	*/
	ValueHolderRef tmp2;
	while(!tmp.empty()) {
		tmp.remove(tmp2);	/* --> tmp2 */
		mailbox.insert(tmp2);	/* <-- tmp2*/
	}
	/*would be better to insert them all at once
	rather than one at a time.
	*/

	/*does not require atomicity, since only one
	worker thread can perform marking on any
	process at a time.
	*/
	P->blacken();
}

/*
 * Actual work
 */

/*please refer to file doc/process-gc.txt*/
void Worker::work(void) {
	ProcessStatus Rstat;
	RunningProcessRef R;
	Process* Q = 0;
	size_t timeslice;

WorkerLoop:
	if(T) {
		/*trigger GC*/
		if(T == 1) {
			if(R) {
				parent->workqueue_push(R);
				R = 0;
			}
			{SoftStop ss(parent);
				/*all other threads are now suspended*/
				SymbolScanner ssc(parent->Ws);
				symbols->traverse_symbols(&ssc);
				for(size_t i; i < parent->total_workers; ++i) {
					parent->gray_workers++; // N
					parent->Ws[i]->gray_done = 0;
					parent->Ws[i]->scanning_mode = 1;
				}
			}
			T = 0;
		} else {
			--T;
		}
	}
	/*this also checks for soft-stop for us*/
	if(R) {
		parent->workqueue_push_and_pop(R);
	} else {
		if(!parent->workqueue_pop(R)) {
			return; //no more work
		}
	}
	if(scanning_mode) {
		if(R->is_black()) {
			scanning_mode = 0;
		} else {
			mark_process(R);
		}
	}
	timeslice = parent->default_timeslice;
execute:
	Rstat = R->execute(timeslice, Q);
	switch(Rstat) {
	case process_waiting:
	case process_dead:
		R = 0; // clear
	case process_change:
		parent->workqueue_push(R);
		R = Q;
		Q = 0;
		if(scanning_mode && !R->is_black()) {
			mark_process(R);
		}
		goto execute;
	}
	if(!scanning_mode && !gray_done) {
		if(!gray_set.empty()) {
			if(R) {
				parent->workqueue_push(R);
				R = 0;
			}
			std::set<Process*>::const_iterator i;
		get_gray:
			i = gray_set.begin();
			Q = *i;
			gray_set.erase(i);
			{AnesthesizeProcess ap(Q, parent);
				if(ap.succeeded) {
					mark_process(Q);
				} else {
					goto get_gray;
				}
			}
			Q = 0;
		} else {
			gray_done = 1;
			if((--parent->gray_workers) == 0) {
				goto Sweep;
			}
		}
	}
	goto WorkerLoop;

Sweep:
	if(R) {
		parent->workqueue_push(R);
		R = 0;
	}
	{SoftStop ss(parent);
		size_t i, j;
		Process* tmp;
		std::vector<Process*>& U = parent->U;
		size_t l = U.size();
		for(i = 0, j = 0; i < l; ++i) {
			if(U[i]->is_black()) {
				U[i]->whiten();
				/*swap in order to partition
				U into known-live and known-dead
				*/
				tmp = U[i];
				U[i] = U[j];
				U[j] = tmp;
				++j;
			} else {
				U[i]->kill();
			}
		}

		/*first clean the symbol's notification lists*/
		SymbolNotificationCleaner snc(U, j);
		symbols->traverse_symbols(&snc);

		/*now really delete U[j] onwards*/
		for(i = j; i < l; ++i) {
			delete U[i];
		}
		U.resize(j);

		size_t died = l - j;
		/*having got the short stick, we now compute
		the trigger point for the next GC
		*/
		T =
		(died >= 4096) ? 	1 :
		/*otherwise*/		(4096 - died);
		T += 1;
		T *= 4;
	}
	goto WorkerLoop;
}

<commit_msg>src/workers.cpp: Added a missing break in switch() form<commit_after>#include"all_defines.hpp"

#include"types.hpp"
#include"workers.hpp"
#include"processes.hpp"
#include"mutexes.hpp"
#include"lockeds.hpp"
#include"symbols.hpp"

#include<boost/noncopyable.hpp>

#include<iostream>

/*-----------------------------------------------------------------------------
AllWorkers
-----------------------------------------------------------------------------*/

/*
 * Registration
 */

void AllWorkers::register_process(Process* P) {
	AppLock l(U_mtx);
	U.push_back(P);
}

void AllWorkers::register_worker(Worker* W) {
	AppLock l(general_mtx);
	Ws.push_back(W);
	total_workers++;
}

void AllWorkers::unregister_worker(Worker* W) {
	AppLock lg(general_mtx);
	size_t l = Ws.size();
	for(size_t i = 0; i < l; ++i) {
		if(Ws[i] == W) {
			Ws[i] = Ws[l - 1];
			Ws.resize(l - 1);
			--total_workers;
			/*check if this has changed any of the
			waiting states
			*/
			if(soft_stop_condition) {
				if(soft_stop_waiting == total_workers) {
					soft_stop_waiting = 0;
					soft_stop_cv.broadcast();
				}
			}
			if(workqueue_waiting > 0) {
				if(workqueue_waiting == total_workers) {
					workqueue_waiting = 0;
					exit_condition = 1;
					workqueue_cv.broadcast();
				}
			}
			return;
		}
	}
}

/*
 * Soft-stop
 */

void AllWorkers::soft_stop_raise(void) {
	AppLock l(general_mtx);
	soft_stop_condition = 1;
	if(workqueue_waiting != 0) {
		workqueue_cv.broadcast();
	}
	soft_stop_waiting++;
	soft_stop_cv.wait(l);
}
void AllWorkers::soft_stop_lower(void) {
	AppLock l(general_mtx);
	soft_stop_condition = 0;
	soft_stop_cv.broadcast();
}

void AllWorkers::soft_stop_check(AppLock& l) {
	while(soft_stop_condition) {
		soft_stop_waiting++;
		if(soft_stop_waiting == total_workers) {
			soft_stop_waiting = 0;
			soft_stop_cv.broadcast();
		} else {
			soft_stop_cv.wait(l);
		}
	}
}

/*
 * Workqueue
 */

void AllWorkers::workqueue_push(Process* R) {
	AppLock l(general_mtx);
	bool sig = workqueue.empty();
	workqueue.push(R);
	if(sig) workqueue_cv.broadcast();
}
void AllWorkers::workqueue_push_and_pop(Process*& R) {
	AppLock l(general_mtx);
	soft_stop_check(l);
	/*if workqueue is empty, we'd end up popping what we
	would have pushed anyway, so just short-circuit it
	*/
	if(workqueue.empty()) return;
	workqueue.push(R);
	R = workqueue.front();
	workqueue.pop();
}
bool AllWorkers::workqueue_pop(Process*& R) {
	AppLock l(general_mtx);
start:
	soft_stop_check(l);
	if(!workqueue.empty()) {
		R = workqueue.front();
		workqueue.pop();
		return 1;
	}
retry:
	workqueue_waiting++;
	if(workqueue_waiting == total_workers) {
		workqueue_waiting = 0;
		exit_condition = 1;
		return 0;
	}
	workqueue_cv.wait(l);
	if(exit_condition) return 0;
	--workqueue_waiting;
	/*if we exited due to a soft-stop, bar*/
	if(soft_stop_condition) goto start;
	if(workqueue.empty()) goto retry;
	R = workqueue.front();
	workqueue.pop();
	return 1;
}

/*-----------------------------------------------------------------------------
Worker
-----------------------------------------------------------------------------*/

void Worker::operator()(void) {
	try {
		try {
			parent->register_worker(this);
			work();
			parent->unregister_worker(this);
		} catch(std::exception& e) {
			std::cerr << "Unhandled exception:" << std::endl;
			std::cerr << e.what() << std::endl;
			std::cerr << "aborting a worker thread..."
				<< std::endl;
			std::cerr.flush();
			parent->unregister_worker(this);
			return;
		}
	} catch(...) {
		std::cerr << "Unknown exception!" << std::endl;
		std::cerr << "aborting a worker thread..."
			<< std::endl;
		std::cerr.flush();
		parent->unregister_worker(this);
		return;
	}
}

/*
 * Helper RAII classes
 */

class MarkingTraverser : public HeapTraverser {
private:
	std::set<Process*>* pgray_set;
public:
	explicit MarkingTraverser(std::set<Process*>& ngray_set)
		: pgray_set(&ngray_set) { }
	virtual void traverse(Generic* gp) {
		HlPid* pp = dynamic_cast<HlPid*>(gp);
		if(pp && pp->process->waiting_and_not_black()){
			pgray_set->insert(pp->process);
		}
	}
};

/*Holds the currently-running process.  If something throws
while we're running that process, just kill the process and
drop its references.
*/
class RunningProcessRef : boost::noncopyable {
public:
	Process* p;
	RunningProcessRef(void) : p(0) { }
	RunningProcessRef& operator=(Process* np) { p = np; }
	~RunningProcessRef() {
		/*DON'T delete p, just kill it*/
		if(p) p->atomic_kill();
	}
	operator Process*&(void) { return p; }

	Process* operator->(void) { return p; }
	Process* operator->(void) const { return p; }
	Process& operator*(void) { return *p; }
	Process& operator*(void) const { return *p; }
};

/*Causes a soft-stop on other worker threads
*/
class SoftStop : boost::noncopyable {
	AllWorkers* parent;
	#ifndef single_threaded
		bool single_threaded_save;
	#endif
public:
	explicit SoftStop(AllWorkers* nparent)
		: parent(nparent) {
		parent->soft_stop_raise();
		#ifndef single_threaded
			single_threaded_save = single_threaded;
			single_threaded = 1;
		#endif
	}
	~SoftStop() {
		#ifndef single_threaded
			single_threaded = single_threaded_save;
		#endif
		parent->soft_stop_lower();
	}
};

class AnesthesizeProcess : boost::noncopyable {
	AllWorkers* parent;
	Process* P;
public:
	bool succeeded;
	AnesthesizeProcess(Process* nP, AllWorkers* nparent)
		: parent(nparent),
		  P(nP),
		  succeeded(nP->anesthesize()) { }
	~AnesthesizeProcess() {
		if(succeeded) {
			if(P->unanesthesize()) {
				/*Process::unanesthesize should have
				set status to process_running
				*/
				parent->workqueue_push(P);
			}
		}
	}
};

/*
 * Marks all PID's of a process
 */
void Worker::mark_process(Process* P) {
	MarkingTraverser mt(gray_set);
	P->heap().traverse_objects(&mt);

	/*scan the mailbox*/
	ValueHolderRef tmp;
	LockedValueHolderRef& mailbox = P->mailbox();
	mailbox.swap(tmp);
	if(!tmp.empty()) {
		/*shouldn't throw: traverse_objects doesn't
		throw, and MarkingTraverser doesn't either
		*/
		tmp->traverse_objects(&mt);
		/*put back the contents into the mailbox*/
		mailbox.swap(tmp);
	}

	/*another process might have sent new messages
	while we were marking - so tmp might not be
	empty.  We just push tmp one-by-one onto the
	mailbox.
	*/
	/*
	We don't have to rescan the new messages - we
	only mark during a process-level GC, and
	messages that are sent while a GC is going on
	cannot contain references to white processes,
	because only black processes are actually
	allowed to run (and by extension, send
	messages).
	*/
	ValueHolderRef tmp2;
	while(!tmp.empty()) {
		tmp.remove(tmp2);	/* --> tmp2 */
		mailbox.insert(tmp2);	/* <-- tmp2*/
	}
	/*would be better to insert them all at once
	rather than one at a time.
	*/

	/*does not require atomicity, since only one
	worker thread can perform marking on any
	process at a time.
	*/
	P->blacken();
}

/*
 * Actual work
 */

/*please refer to file doc/process-gc.txt*/
void Worker::work(void) {
	ProcessStatus Rstat;
	RunningProcessRef R;
	Process* Q = 0;
	size_t timeslice;

WorkerLoop:
	if(T) {
		/*trigger GC*/
		if(T == 1) {
			if(R) {
				parent->workqueue_push(R);
				R = 0;
			}
			{SoftStop ss(parent);
				/*all other threads are now suspended*/
				SymbolScanner ssc(parent->Ws);
				symbols->traverse_symbols(&ssc);
				for(size_t i; i < parent->total_workers; ++i) {
					parent->gray_workers++; // N
					parent->Ws[i]->gray_done = 0;
					parent->Ws[i]->scanning_mode = 1;
				}
			}
			T = 0;
		} else {
			--T;
		}
	}
	/*this also checks for soft-stop for us*/
	if(R) {
		parent->workqueue_push_and_pop(R);
	} else {
		if(!parent->workqueue_pop(R)) {
			return; //no more work
		}
	}
	if(scanning_mode) {
		if(R->is_black()) {
			scanning_mode = 0;
		} else {
			mark_process(R);
		}
	}
	timeslice = parent->default_timeslice;
execute:
	Rstat = R->execute(timeslice, Q);
	switch(Rstat) {
	case process_waiting:
	case process_dead:
		R = 0; // clear
		break;
	case process_change:
		parent->workqueue_push(R);
		R = Q;
		Q = 0;
		if(scanning_mode && !R->is_black()) {
			mark_process(R);
		}
		goto execute;
	}
	if(!scanning_mode && !gray_done) {
		if(!gray_set.empty()) {
			if(R) {
				parent->workqueue_push(R);
				R = 0;
			}
			std::set<Process*>::const_iterator i;
		get_gray:
			i = gray_set.begin();
			Q = *i;
			gray_set.erase(i);
			{AnesthesizeProcess ap(Q, parent);
				if(ap.succeeded) {
					mark_process(Q);
				} else {
					goto get_gray;
				}
			}
			Q = 0;
		} else {
			gray_done = 1;
			if((--parent->gray_workers) == 0) {
				goto Sweep;
			}
		}
	}
	goto WorkerLoop;

Sweep:
	if(R) {
		parent->workqueue_push(R);
		R = 0;
	}
	{SoftStop ss(parent);
		size_t i, j;
		Process* tmp;
		std::vector<Process*>& U = parent->U;
		size_t l = U.size();
		for(i = 0, j = 0; i < l; ++i) {
			if(U[i]->is_black()) {
				U[i]->whiten();
				/*swap in order to partition
				U into known-live and known-dead
				*/
				tmp = U[i];
				U[i] = U[j];
				U[j] = tmp;
				++j;
			} else {
				U[i]->kill();
			}
		}

		/*first clean the symbol's notification lists*/
		SymbolNotificationCleaner snc(U, j);
		symbols->traverse_symbols(&snc);

		/*now really delete U[j] onwards*/
		for(i = j; i < l; ++i) {
			delete U[i];
		}
		U.resize(j);

		size_t died = l - j;
		/*having got the short stick, we now compute
		the trigger point for the next GC
		*/
		T =
		(died >= 4096) ? 	1 :
		/*otherwise*/		(4096 - died);
		T += 1;
		T *= 4;
	}
	goto WorkerLoop;
}

<|endoftext|>


<commit_before>#include "RoleBase.h"
#include "Creature.h"
#include "interface.h"
#include "Action.h"
#include "Utils.h"
#include "NavigationFinder.h"
#include "Engine.h"
#include "World.h"
#include "Object.h"
#include "Avatar.h"

CRoleBase::CRoleBase(void)
{
	m_pCreature = NULL;
	m_pActionComplete = NULL;
	m_nOrceDirection = 0;
	m_nMoveing = 0;
	m_fMoveSpeed = 6.0f;
	m_nMoveToType = 0;
	m_nUpdateMove = 1;
}

CRoleBase::~CRoleBase(void)
{
	SAFE_DELETE(m_pActionComplete);
	SAFE_DELETE(m_pCreature);
	SAFE_DELETE(m_pPathFind);
}

//ʼɫ
int CRoleBase::Init(int nRoleID, const char* strCharFile)
{
	if (m_pCreature)return 1;

	m_nMoveing = 1;
	m_nRoleID = nRoleID;
	m_pCreature = new CCreature();
	m_pActionComplete = MakeInterface(this, &CRoleBase::OnActionMsg);
	m_pCreature->SetActionComplete(m_pActionComplete);

	m_pCreature->Load(strCharFile);
	m_pCreature->SetupBody(1, 0);
	m_pCreature->SetPosition(vec3_zero);
	m_pCreature->SetDirection(vec3(0.0f, -1.0f, 0.0f));
	m_pCreature->SetRoleID(nRoleID);

	m_AngleYaw.SetDirection(vec3(0.0f, -1.0f, 0.0f));

	m_pPathFind = new CNavigationFinder();
	m_pPathFind->SetMode(CNavigationFinder::MODE_PATHFIND_STRAIGHT);

	return 1;
}

void CRoleBase::Update(float ifps)
{
	if (NULL == m_pCreature)return;

	if (m_nUpdateMove)
	{
		m_AngleYaw.Update(ifps);
		UpdateMove(ifps);
		m_pCreature->SetDirection(m_AngleYaw.GetForwardDirection(), m_nOrceDirection);
	}

	m_pCreature->Update();
}

int CRoleBase::OnActionMsg(void* pVoid)
{
	_ActionCallback* pInfo = (_ActionCallback*)pVoid;

	switch (pInfo->nMsgID)
	{
	case 0:
	{
		OnKeyFrame((_ActionCallback_KeyFrame*)pVoid);
	}break;
	case 1:
	{
		OnActionComplete((_ActionCallback_Complete*)pVoid);
	}break;
	}

	return 1;
}
//ؼ֡ص
void CRoleBase::OnKeyFrame(_ActionCallback_KeyFrame* pKeyInfo)
{

}//ɻص
void CRoleBase::OnActionComplete(_ActionCallback_Complete* pActInfo)
{
	if (NULL == m_pCreature)return;

	if (m_nMoveing)
	{
		m_pCreature->PlayAction("run");
	}
	else
	{
		m_pCreature->PlayAction("stand");
	}
}
CAction* CRoleBase::PlayAction(const char* szName, int nLoop, float fCorrectingTime /*= 1.0f*/)
{
	if (NULL == m_pCreature)return NULL;

	if (1 == m_pCreature->PlayAction(szName, nLoop, fCorrectingTime))
	{
		return m_pCreature->GetNowAction();
	}

	return NULL;
}
CAction* CRoleBase::OrceAction(const char* szName, int nLoop, float fCorrectingTime /*= 1.0f*/)
{
	if (NULL == m_pCreature)return NULL;

	if (1 == m_pCreature->OrceAction(szName, nLoop, fCorrectingTime))
	{
		return m_pCreature->GetNowAction();
	}

	return NULL;
}
<commit_msg>SetPostion<commit_after>#include "RoleBase.h"
#include "Creature.h"
#include "interface.h"
#include "Action.h"
#include "Utils.h"
#include "NavigationFinder.h"
#include "Engine.h"
#include "World.h"
#include "Object.h"
#include "Avatar.h"

CRoleBase::CRoleBase(void)
{
	m_pCreature = NULL;
	m_pActionComplete = NULL;
	m_nOrceDirection = 0;
	m_nMoveing = 0;
	m_fMoveSpeed = 6.0f;
	m_nMoveToType = 0;
	m_nUpdateMove = 1;
}

CRoleBase::~CRoleBase(void)
{
	SAFE_DELETE(m_pActionComplete);
	SAFE_DELETE(m_pCreature);
	SAFE_DELETE(m_pPathFind);
}

//ʼɫ
int CRoleBase::Init(int nRoleID, const char* strCharFile)
{
	if (m_pCreature)return 1;

	m_nMoveing = 1;
	m_nRoleID = nRoleID;
	m_pCreature = new CCreature();
	m_pActionComplete = MakeInterface(this, &CRoleBase::OnActionMsg);
	m_pCreature->SetActionComplete(m_pActionComplete);

	m_pCreature->Load(strCharFile);
	m_pCreature->SetupBody(1, 0);
	m_pCreature->SetPosition(vec3_zero);
	m_pCreature->SetDirection(vec3(0.0f, -1.0f, 0.0f));
	m_pCreature->SetRoleID(nRoleID);

	m_AngleYaw.SetDirection(vec3(0.0f, -1.0f, 0.0f));

	m_pPathFind = new CNavigationFinder();
	m_pPathFind->SetMode(CNavigationFinder::MODE_PATHFIND_STRAIGHT);

	return 1;
}

void CRoleBase::Update(float ifps)
{
	if (NULL == m_pCreature)return;

	if (m_nUpdateMove)
	{
		m_AngleYaw.Update(ifps);
		UpdateMove(ifps);
		m_pCreature->SetDirection(m_AngleYaw.GetForwardDirection(), m_nOrceDirection);
	}

	m_pCreature->Update();
}

int CRoleBase::OnActionMsg(void* pVoid)
{
	_ActionCallback* pInfo = (_ActionCallback*)pVoid;

	switch (pInfo->nMsgID)
	{
	case 0:
	{
		OnKeyFrame((_ActionCallback_KeyFrame*)pVoid);
	}break;
	case 1:
	{
		OnActionComplete((_ActionCallback_Complete*)pVoid);
	}break;
	}

	return 1;
}
//ؼ֡ص
void CRoleBase::OnKeyFrame(_ActionCallback_KeyFrame* pKeyInfo)
{

}//ɻص
void CRoleBase::OnActionComplete(_ActionCallback_Complete* pActInfo)
{
	if (NULL == m_pCreature)return;

	if (m_nMoveing)
	{
		m_pCreature->PlayAction("run");
	}
	else
	{
		m_pCreature->PlayAction("stand");
	}
}
CAction* CRoleBase::PlayAction(const char* szName, int nLoop, float fCorrectingTime /*= 1.0f*/)
{
	if (NULL == m_pCreature)return NULL;

	if (1 == m_pCreature->PlayAction(szName, nLoop, fCorrectingTime))
	{
		return m_pCreature->GetNowAction();
	}

	return NULL;
}
CAction* CRoleBase::OrceAction(const char* szName, int nLoop, float fCorrectingTime /*= 1.0f*/)
{
	if (NULL == m_pCreature)return NULL;

	if (1 == m_pCreature->OrceAction(szName, nLoop, fCorrectingTime))
	{
		return m_pCreature->GetNowAction();
	}

	return NULL;
}
void CRoleBase::SetPosition(const vec3& vPosition, int nOrce /*= 0*/)
{
	if (NULL == m_pCreature)return;

	m_pCreature->SetPosition(vPosition, nOrce);
}

void CRoleBase::SetDirection(const vec3& vDirection, int nOrce /*= 0*/)
{
	if (NULL == m_pCreature)return;
	if (m_pCreature->GetNowAction()->GetLockMove() && !nOrce)return;

	m_nOrceDirection = nOrce;
	m_AngleYaw.SetDirection(vDirection);
}<|endoftext|>


<commit_before>#include <QSizePolicy>
#include <QSpacerItem>

#include <QtDebug>

#include "librarywidget.h"
#include "ui_librarywidget.h"

LibraryWidget::LibraryWidget(QWidget* const parent, AppController* const controller)
    : QWidget(parent)
    , ui(new Ui::LibraryWidget)
    , controller(controller)
{
    ui->setupUi(this);

    tabGameFrames << new QList<GameFrame*> << new QList<GameFrame*>;

    tabScrollAreas << new QScrollArea() << new QScrollArea();
    tabWrappers << new QWidget << new QWidget;
    tabLayouts << new QVBoxLayout() << new QVBoxLayout();
    tabRowLayouts << new QList<QVBoxLayout*> << new QList<QVBoxLayout*>;
    tabColLayouts << new QList<QHBoxLayout*> << new QList<QHBoxLayout*>;

    for(int i = 0; i < tabScrollAreas.size(); i++){
        tabScrollAreas.at(i)->setObjectName("gamesScroller");
        tabWrappers.at(i)->setObjectName("scrollWrapper");
    }

    controller->api->myOwnedKeys([this](QList<DownloadKey> keys) {
        onMyPurchasedKeys(keys);
    });

    controller->api->myClaimedKeys([this](QList<DownloadKey> keys) {
        onMyClaimedKeys(keys);
    });

    updateTimer = new QTimer(this);

    connect(updateTimer, &QTimer::timeout, this, &LibraryWidget::onUpdate);
    updateTimer->start(60000);
}

void LibraryWidget::onMyPurchasedKeys(QList<DownloadKey> downloadKeys)
{
    QList<GameFrame*> gameFrames;

    int repeatCount = 0;

    foreach (DownloadKey key, downloadKeys) {
        for(int i = 0; i < tabGameFrames.at(0)->size(); i++){
            if(key.game.id == tabGameFrames.at(0)->at(i)->game.id){
                repeatCount++;
            }
        }

        if(repeatCount == 0){
            gameFrames << new GameFrame(this, key.game, key, controller);
        }
    }

    if(!gameFrames.isEmpty()){
        tabGameFrames.at(0)->append(gameFrames);
    }
    if(tabSetupCount < 2){
        addGamesTab("Purchased", 0);
    }
}

void LibraryWidget::onMyClaimedKeys(QList<DownloadKey> downloadKeys)
{
    QList<GameFrame*> gameFrames;

    int repeatCount = 0;

    foreach (DownloadKey key, downloadKeys) {
        for(int i = 0; i < tabGameFrames.at(1)->size(); i++){
            if(key.game.id == tabGameFrames.at(1)->at(i)->game.id){
                repeatCount++;
            }
        }

        if(repeatCount == 0){
            gameFrames << new GameFrame(this, key.game, key, controller);
        }
    }

    if(!gameFrames.isEmpty()){
        tabGameFrames.at(1)->append(gameFrames);
    }
    if(tabSetupCount < 2){
        addGamesTab("Claimed", 1);
    }
}

void LibraryWidget::addGamesTab(const QString& title, int tab)
{
    tabRowLayouts.at(tab)->append(new QVBoxLayout);
    tabColLayouts.at(tab)->append(new QHBoxLayout);

    tabLayouts.at(tab)->addLayout(tabRowLayouts.at(tab)->at(0));
    tabRowLayouts.at(tab)->at(0)->addLayout(tabColLayouts.at(tab)->at(0));

    maxGamesFramesPerRow = (ui->tabWidget->width() - 80) / gameFrameWidth;
    int i = 1;
    int j = 0;

    while(i < tabGameFrames.at(tab)->size() + 1) {
        tabColLayouts.at(tab)->at(j)->addWidget(tabGameFrames.at(tab)->at(i - 1));

        if(i % maxGamesFramesPerRow == 0) {
            tabColLayouts.at(tab)->append(new QHBoxLayout);

            j++;

            tabRowLayouts.at(tab)->at(0)->addLayout(tabColLayouts.at(tab)->at(j));
        }

        i++;
    }

    int p = maxGamesFramesPerRow - (tabGameFrames.at(tab)->size() % maxGamesFramesPerRow);

    while(p > 0) {
        tabColLayouts.at(tab)->at(j)->addSpacerItem(new QSpacerItem(gameFrameWidth, 0));

        p--;
    }

    tabWrappers.at(tab)->setLayout(tabLayouts.at(tab));
    tabScrollAreas.at(tab)->setWidget(tabWrappers.at(tab));

    ui->tabWidget->addTab(tabScrollAreas.at(tab), title);

    tabSetupCount++;
}

void LibraryWidget::resizeEvent(QResizeEvent *event)
{
    event->accept();

    if(tabSetupCount == 2) {
        adjustTabLayouts();
    }
}

void LibraryWidget::adjustTabLayouts()
{
    if((ui->tabWidget->width() - 80) / gameFrameWidth != maxGamesFramesPerRow){
        maxGamesFramesPerRow = (ui->tabWidget->width() - 80) / gameFrameWidth;

        tabWrappers.clear();
        tabLayouts.clear();

        for(int i = 0; i < tabScrollAreas.size(); i++){
            tabRowLayouts.at(i)->clear();
            tabColLayouts.at(i)->clear();
        }

        tabWrappers << new QWidget << new QWidget;
        tabLayouts << new QVBoxLayout() << new QVBoxLayout();
        tabRowLayouts << new QList<QVBoxLayout*> << new QList<QVBoxLayout*>;
        tabColLayouts << new QList<QHBoxLayout*> << new QList<QHBoxLayout*>;

        for(int k = 0; k < tabScrollAreas.size(); k++){
            tabWrappers.at(k)->setObjectName("scrollWrapper");

            tabRowLayouts.at(k)->append(new QVBoxLayout);
            tabColLayouts.at(k)->append(new QHBoxLayout);

            tabLayouts.at(k)->addLayout(tabRowLayouts.at(k)->at(0));
            tabRowLayouts.at(k)->at(0)->addLayout(tabColLayouts.at(k)->at(0));

            int i = 1;
            int j = 0;

            while(i < tabGameFrames.at(k)->size() + 1) {
                tabColLayouts.at(k)->at(j)->addWidget(tabGameFrames.at(k)->at(i - 1));

                if(i % maxGamesFramesPerRow == 0) {
                    tabColLayouts.at(k)->append(new QHBoxLayout);

                    j++;

                    tabRowLayouts.at(k)->at(0)->addLayout(tabColLayouts.at(k)->at(j));
                }

                i++;
            }

            int p = maxGamesFramesPerRow - (tabGameFrames.at(k)->size() % maxGamesFramesPerRow);
            while(p > 0) {
                tabColLayouts.at(k)->at(j)->addSpacerItem(new QSpacerItem(gameFrameWidth, 0));

                p--;
            }

            tabWrappers.at(k)->setLayout(tabLayouts.at(k));
            tabScrollAreas.at(k)->setWidget(tabWrappers.at(k));
        }
    }
}

void LibraryWidget::onUpdate()
{
    QList<int> gameCount;

    for(int i = 0; i < tabGameFrames.size(); i++){
        gameCount.append(tabGameFrames.at(i)->size());
    }

    controller->api->myOwnedKeys([this](QList<DownloadKey> keys) {
        onMyPurchasedKeys(keys);
    });

    controller->api->myClaimedKeys([this](QList<DownloadKey> keys) {
        onMyClaimedKeys(keys);
    });

    for(int i = 0; i < tabGameFrames.size(); i++){
        if(gameCount.at(i) != tabGameFrames.at(i)->size()){
            adjustTabLayouts();
            break;
        }
    }
}

LibraryWidget::~LibraryWidget()
{
    delete ui;
}
<commit_msg>Library updates now send tray notifications.<commit_after>#include <QSizePolicy>
#include <QSpacerItem>

#include <QtDebug>

#include "librarywidget.h"
#include "ui_librarywidget.h"

LibraryWidget::LibraryWidget(QWidget* const parent, AppController* const controller)
    : QWidget(parent)
    , ui(new Ui::LibraryWidget)
    , controller(controller)
{
    ui->setupUi(this);

    tabGameFrames << new QList<GameFrame*> << new QList<GameFrame*>;

    tabScrollAreas << new QScrollArea() << new QScrollArea();
    tabWrappers << new QWidget << new QWidget;
    tabLayouts << new QVBoxLayout() << new QVBoxLayout();
    tabRowLayouts << new QList<QVBoxLayout*> << new QList<QVBoxLayout*>;
    tabColLayouts << new QList<QHBoxLayout*> << new QList<QHBoxLayout*>;

    for(int i = 0; i < tabScrollAreas.size(); i++){
        tabScrollAreas.at(i)->setObjectName("gamesScroller");
        tabWrappers.at(i)->setObjectName("scrollWrapper");
    }

    controller->api->myOwnedKeys([this](QList<DownloadKey> keys) {
        onMyPurchasedKeys(keys);
    });

    controller->api->myClaimedKeys([this](QList<DownloadKey> keys) {
        onMyClaimedKeys(keys);
    });

    updateTimer = new QTimer(this);

    connect(updateTimer, &QTimer::timeout, this, &LibraryWidget::onUpdate);
    updateTimer->start(60000);
}

void LibraryWidget::onMyPurchasedKeys(QList<DownloadKey> downloadKeys)
{
    QList<GameFrame*> gameFrames;

    int repeatCount = 0;

    foreach (DownloadKey key, downloadKeys) {
        for(int i = 0; i < tabGameFrames.at(0)->size(); i++){
            if(key.game.id == tabGameFrames.at(0)->at(i)->game.id){
                repeatCount++;
            }
        }

        if(repeatCount == 0){
            gameFrames << new GameFrame(this, key.game, key, controller);
        }
    }

    if(!gameFrames.isEmpty()){
        tabGameFrames.at(0)->append(gameFrames);
    }
    if(tabSetupCount < 2){
        addGamesTab("Purchased", 0);
    }
}

void LibraryWidget::onMyClaimedKeys(QList<DownloadKey> downloadKeys)
{
    QList<GameFrame*> gameFrames;

    int repeatCount = 0;

    foreach (DownloadKey key, downloadKeys) {
        for(int i = 0; i < tabGameFrames.at(1)->size(); i++){
            if(key.game.id == tabGameFrames.at(1)->at(i)->game.id){
                repeatCount++;
            }
        }

        if(repeatCount == 0){
            gameFrames << new GameFrame(this, key.game, key, controller);
        }
    }

    if(!gameFrames.isEmpty()){
        tabGameFrames.at(1)->append(gameFrames);
    }
    if(tabSetupCount < 2){
        addGamesTab("Claimed", 1);
    }
}

void LibraryWidget::addGamesTab(const QString& title, int tab)
{
    tabRowLayouts.at(tab)->append(new QVBoxLayout);
    tabColLayouts.at(tab)->append(new QHBoxLayout);

    tabLayouts.at(tab)->addLayout(tabRowLayouts.at(tab)->at(0));
    tabRowLayouts.at(tab)->at(0)->addLayout(tabColLayouts.at(tab)->at(0));

    maxGamesFramesPerRow = (ui->tabWidget->width() - 80) / gameFrameWidth;
    int i = 1;
    int j = 0;

    while(i < tabGameFrames.at(tab)->size() + 1) {
        tabColLayouts.at(tab)->at(j)->addWidget(tabGameFrames.at(tab)->at(i - 1));

        if(i % maxGamesFramesPerRow == 0) {
            tabColLayouts.at(tab)->append(new QHBoxLayout);

            j++;

            tabRowLayouts.at(tab)->at(0)->addLayout(tabColLayouts.at(tab)->at(j));
        }

        i++;
    }

    int p = maxGamesFramesPerRow - (tabGameFrames.at(tab)->size() % maxGamesFramesPerRow);

    while(p > 0) {
        tabColLayouts.at(tab)->at(j)->addSpacerItem(new QSpacerItem(gameFrameWidth, 0));

        p--;
    }

    tabWrappers.at(tab)->setLayout(tabLayouts.at(tab));
    tabScrollAreas.at(tab)->setWidget(tabWrappers.at(tab));

    ui->tabWidget->addTab(tabScrollAreas.at(tab), title);

    tabSetupCount++;
}

void LibraryWidget::resizeEvent(QResizeEvent *event)
{
    event->accept();

    if(tabSetupCount == 2) {
        adjustTabLayouts();
    }
}

void LibraryWidget::adjustTabLayouts()
{
    if((ui->tabWidget->width() - 80) / gameFrameWidth != maxGamesFramesPerRow){
        maxGamesFramesPerRow = (ui->tabWidget->width() - 80) / gameFrameWidth;

        tabWrappers.clear();
        tabLayouts.clear();

        for(int i = 0; i < tabScrollAreas.size(); i++){
            tabRowLayouts.at(i)->clear();
            tabColLayouts.at(i)->clear();
        }

        tabWrappers << new QWidget << new QWidget;
        tabLayouts << new QVBoxLayout() << new QVBoxLayout();
        tabRowLayouts << new QList<QVBoxLayout*> << new QList<QVBoxLayout*>;
        tabColLayouts << new QList<QHBoxLayout*> << new QList<QHBoxLayout*>;

        for(int k = 0; k < tabScrollAreas.size(); k++){
            tabWrappers.at(k)->setObjectName("scrollWrapper");

            tabRowLayouts.at(k)->append(new QVBoxLayout);
            tabColLayouts.at(k)->append(new QHBoxLayout);

            tabLayouts.at(k)->addLayout(tabRowLayouts.at(k)->at(0));
            tabRowLayouts.at(k)->at(0)->addLayout(tabColLayouts.at(k)->at(0));

            int i = 1;
            int j = 0;

            while(i < tabGameFrames.at(k)->size() + 1) {
                tabColLayouts.at(k)->at(j)->addWidget(tabGameFrames.at(k)->at(i - 1));

                if(i % maxGamesFramesPerRow == 0) {
                    tabColLayouts.at(k)->append(new QHBoxLayout);

                    j++;

                    tabRowLayouts.at(k)->at(0)->addLayout(tabColLayouts.at(k)->at(j));
                }

                i++;
            }

            int p = maxGamesFramesPerRow - (tabGameFrames.at(k)->size() % maxGamesFramesPerRow);
            while(p > 0) {
                tabColLayouts.at(k)->at(j)->addSpacerItem(new QSpacerItem(gameFrameWidth, 0));

                p--;
            }

            tabWrappers.at(k)->setLayout(tabLayouts.at(k));
            tabScrollAreas.at(k)->setWidget(tabWrappers.at(k));
        }
    }
}

void LibraryWidget::onUpdate()
{
    QList<int> gameCount;

    for(int i = 0; i < tabGameFrames.size(); i++){
        gameCount.append(tabGameFrames.at(i)->size());
    }

    controller->api->myOwnedKeys([this](QList<DownloadKey> keys) {
        onMyPurchasedKeys(keys);
    });

    controller->api->myClaimedKeys([this](QList<DownloadKey> keys) {
        onMyClaimedKeys(keys);
    });

    for(int i = 0; i < tabGameFrames.size(); i++){
        if(gameCount.at(i) != tabGameFrames.at(i)->size()){
            adjustTabLayouts();

            controller->showTrayIconNotification(TrayNotifications::LIBRARY_UPDATE_AVAILABLE,
                                                 "New games are available to download.");

            break;
        }
    }
}

LibraryWidget::~LibraryWidget()
{
    delete ui;
}
<|endoftext|>


<commit_before>/*
 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
 *
 * File: matlab/private/test.cpp
 *
 * Copyright 2016 Patrik Huber
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mexplus.h"

#include "mex.h"
//#include "matrix.h"

#include <iostream>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
	// Check for proper number of input and output arguments:
	mexPrintf("nlhs: %d, nrhs: %d\n", nlhs, nrhs);
	if (nrhs != 2) {
		mexErrMsgIdAndTxt("eos:example:nargin", "example requires two input arguments.");
	}
	else if (nlhs >= 1) {
		mexErrMsgIdAndTxt("eos:example:nargout", "example requires no output argument.");
	}

	double *vin1, *vin2;
	vin1 = (double*)mxGetPr(prhs[0]);
	vin2 = (double*)mxGetPr(prhs[1]);
	mexPrintf("%f, %f\n", *vin1, *vin2);
};

void func()
{
	int x = 4;
};

int func1()
{
	return 5;
};

class MyClass
{
public:
	MyClass() = default;
	int test() {
		return 6;
	};
};
<commit_msg>Working prototype of Eigen <=> Matlab conversion<commit_after>/*
 * eos - A 3D Morphable Model fitting library written in modern C++11/14.
 *
 * File: matlab/private/test.cpp
 *
 * Copyright 2016 Patrik Huber
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "mexplus.h"

#include "Eigen/Core"

#include "mex.h"
//#include "matrix.h"

#include <iostream>

namespace mexplus {
// Define template specialisations for Eigen::MatrixXd:
template<>
mxArray* MxArray::from(const Eigen::MatrixXd& eigen_matrix) {
	const int num_rows = static_cast<int>(eigen_matrix.rows());
	const int num_cols = static_cast<int>(eigen_matrix.cols());
	MxArray out_array(MxArray::Numeric<double>(num_rows, num_cols));

	// This might not copy the data but it's evil and probably really dangerous!!!:
	//mxSetData(const_cast<mxArray*>(matrix.get()), (void*)value.data());

	// This copies the data. But I suppose it makes sense that we copy the data when we go
	// from C++ to Matlab, since Matlab can unload the C++ mex module at any time I think.
	// Loop is column-wise
	for (int c = 0; c < num_cols; ++c) {
		for (int r = 0; r < num_rows; ++r) {
			out_array.set(r, c, eigen_matrix(r, c));
		}
	}
	return out_array.release();
};

template<>
void MxArray::to(const mxArray* in_array, Eigen::MatrixXd* eigen_matrix)
{
	MxArray array(in_array);

	if (array.dimensionSize() > 2)
	{
		mexErrMsgIdAndTxt("eos:matlab", "Given array has > 2 dimensions. Can only create 2-dimensional matrices (and vectors).");
	}

	if (array.dimensionSize() == 1 || array.dimensionSize() == 0)
	{
		mexErrMsgIdAndTxt("eos:matlab", "Given array has 0 or 1 dimensions but we expected a 2-dimensional matrix (or row/column vector).");
		// Even when given a single value dimensionSize() is 2, with n=m=1. When does this happen?
	}

	if (!array.isDouble())
	{
		mexErrMsgIdAndTxt("eos:matlab", "Trying to create a Eigen::MatrixXd in C++, but the given data is not of type double.");
	}

	// We can be sure now that the array is 2-dimensional (or 0, but then we're screwed anyway)
	auto nrows = array.dimensions()[0]; // or use array.rows()
	auto ncols = array.dimensions()[1];

	// I think I can just use Eigen::Matrix, not a Map - the Matrix c'tor that we call creates a Map anyway?
	Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>> eigen_map(array.getData<double>(), nrows, ncols);
	// Not sure that's alright - who owns the data? I think as it is now, everything points to the data in the mxArray owned by Matlab, but I'm not 100% sure.
	*eigen_matrix = eigen_map;
};

} // namespace mexplus

using namespace mexplus;

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
	using std::vector;
	// Check for proper number of input and output arguments:
	mexPrintf("nlhs: %d, nrhs: %d\n", nlhs, nrhs);
	if (nrhs != 2) {
		mexErrMsgIdAndTxt("eos:example:nargin", "Example requires two input arguments.");
	}
	else if (nlhs >= 2) { // 'nlhs >= 1' means no output argument apparently?
		mexErrMsgIdAndTxt("eos:example:nargout", "Example requires zero or one output arguments.");
	}

	InputArguments input(nrhs, prhs, 2);
	double vin1 = input.get<double>(0);
	// Matlab stores col-wise in memory - hence the entry of the second row comes first
	auto vin2 = input.get<vector<double>>(1);
/*	auto test = input[1];
	MxArray mxa(test);
	auto ndim = mxa.dimensionSize();
	auto nrows = mxa.dimensions()[0];
	auto ncols = mxa.dimensions()[1];
	Eigen::Map<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::ColMajor>> em(vin2.data(), 2, 3);
	// ==> Yes, now I can put exactly this in the MxArray namespace!
	std::stringstream ss;
	ss << em;
	std::string msg = ss.str();
*/
	//auto x = MxArray::Numeric<double>(2, 2);

	auto asdf = input.get<Eigen::MatrixXd>(1);
	std::stringstream ss2;
	ss2 << asdf;
	std::string msg2 = ss2.str();

	OutputArguments output(nlhs, plhs, 1);
	output.set(0, asdf);

	//double *vin1, *vin2;
	//vin1 = (double*)mxGetPr(prhs[0]);
	//vin2 = (double*)mxGetPr(prhs[1]);
	mexPrintf("%f, %f\n", vin1, vin2[0]);
};

void func()
{
	int x = 4;
};

int func1()
{
	return 5;
};

class MyClass
{
public:
	MyClass() = default;
	int test() {
		return 6;
	};
};
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012-2016 Max Kellermann <max@duempel.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef FILE_DESCRIPTOR_HXX
#define FILE_DESCRIPTOR_HXX

#include <inline/compiler.h>

#include <utility>

#include <assert.h>
#include <unistd.h>
#include <sys/types.h>

#ifdef __linux__
#define HAVE_POSIX
#define HAVE_EVENTFD
#define HAVE_SIGNALFD
#define HAVE_INOTIFY
#include <signal.h>
#endif

/**
 * An OO wrapper for a UNIX file descriptor.
 *
 * This class is unmanaged and trivial; for a managed version, see
 * #UniqueFileDescriptor.
 */
class FileDescriptor {
protected:
	int fd;

public:
	FileDescriptor() = default;
	explicit constexpr FileDescriptor(int _fd):fd(_fd) {}

	constexpr bool operator==(FileDescriptor other) const {
		return fd == other.fd;
	}

	constexpr bool operator!=(FileDescriptor other) const {
		return fd != other.fd;
	}

	constexpr bool IsDefined() const {
		return fd >= 0;
	}

	/**
	 * Returns the file descriptor.  This may only be called if
	 * IsDefined() returns true.
	 */
	constexpr int Get() const {
		return fd;
	}

	void Set(int _fd) {
		fd = _fd;
	}

	int Steal() {
		assert(IsDefined());

		return std::exchange(fd, -1);
	}

	void SetUndefined() {
		fd = -1;
	}

	static constexpr FileDescriptor Undefined() {
		return FileDescriptor(-1);
	}

	bool Open(const char *pathname, int flags, mode_t mode=0666);
	bool OpenReadOnly(const char *pathname);

#ifdef HAVE_POSIX
	bool OpenNonBlocking(const char *pathname);

	static bool CreatePipe(FileDescriptor &r, FileDescriptor &w);

	/**
	 * Enable non-blocking mode on this file descriptor.
	 */
	void SetNonBlocking();

	/**
	 * Enable blocking mode on this file descriptor.
	 */
	void SetBlocking();

	/**
	 * Auto-close this file descriptor when a new program is
	 * executed.
	 */
	void EnableCloseOnExec();

	/**
	 * Do not auto-close this file descriptor when a new program
	 * is executed.
	 */
	void DisableCloseOnExec();

	/**
	 * Duplicate the file descriptor onto the given file descriptor.
	 */
	bool Duplicate(FileDescriptor new_fd) const {
		return ::dup2(Get(), new_fd.Get()) == 0;
	}

	/**
	 * Similar to Duplicate(), but if destination and source file
	 * descriptor are equal, clear the close-on-exec flag.  Use
	 * this method to inject file descriptors into a new child
	 * process, to be used by a newly executed program.
	 */
	bool CheckDuplicate(FileDescriptor new_fd);
#endif

#ifdef HAVE_EVENTFD
	bool CreateEventFD(unsigned initval=0);
#endif

#ifdef HAVE_SIGNALFD
	bool CreateSignalFD(const sigset_t *mask);
#endif

#ifdef HAVE_INOTIFY
	bool CreateInotify();
#endif

	/**
	 * Close the file descriptor.  It should not be called on an
	 * "undefined" object.  After this call, IsDefined() is guaranteed
	 * to return false, and this object may be reused.
	 */
	bool Close() {
		return ::close(Steal()) == 0;
	}

	/**
	 * Rewind the pointer to the beginning of the file.
	 */
	bool Rewind();

	off_t Seek(off_t offset) {
		return lseek(Get(), offset, SEEK_SET);
	}

	off_t Skip(off_t offset) {
		return lseek(Get(), offset, SEEK_CUR);
	}

	gcc_pure
	off_t Tell() const {
		return lseek(Get(), 0, SEEK_CUR);
	}

	/**
	 * Returns the size of the file in bytes, or -1 on error.
	 */
	gcc_pure
	off_t GetSize() const;

	ssize_t Read(void *buffer, size_t length) {
		return ::read(fd, buffer, length);
	}

	ssize_t Write(const void *buffer, size_t length) {
		return ::write(fd, buffer, length);
	}

#ifdef HAVE_POSIX
	int Poll(short events, int timeout) const;

	int WaitReadable(int timeout) const;
	int WaitWritable(int timeout) const;

	gcc_pure
	bool IsReadyForWriting() const;
#endif
};

#endif
<commit_msg>io/FileDescriptor: remove assertion from Steal(), allow on undefined instance<commit_after>/*
 * Copyright (C) 2012-2016 Max Kellermann <max@duempel.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef FILE_DESCRIPTOR_HXX
#define FILE_DESCRIPTOR_HXX

#include <inline/compiler.h>

#include <utility>

#include <assert.h>
#include <unistd.h>
#include <sys/types.h>

#ifdef __linux__
#define HAVE_POSIX
#define HAVE_EVENTFD
#define HAVE_SIGNALFD
#define HAVE_INOTIFY
#include <signal.h>
#endif

/**
 * An OO wrapper for a UNIX file descriptor.
 *
 * This class is unmanaged and trivial; for a managed version, see
 * #UniqueFileDescriptor.
 */
class FileDescriptor {
protected:
	int fd;

public:
	FileDescriptor() = default;
	explicit constexpr FileDescriptor(int _fd):fd(_fd) {}

	constexpr bool operator==(FileDescriptor other) const {
		return fd == other.fd;
	}

	constexpr bool operator!=(FileDescriptor other) const {
		return fd != other.fd;
	}

	constexpr bool IsDefined() const {
		return fd >= 0;
	}

	/**
	 * Returns the file descriptor.  This may only be called if
	 * IsDefined() returns true.
	 */
	constexpr int Get() const {
		return fd;
	}

	void Set(int _fd) {
		fd = _fd;
	}

	int Steal() {
		return std::exchange(fd, -1);
	}

	void SetUndefined() {
		fd = -1;
	}

	static constexpr FileDescriptor Undefined() {
		return FileDescriptor(-1);
	}

	bool Open(const char *pathname, int flags, mode_t mode=0666);
	bool OpenReadOnly(const char *pathname);

#ifdef HAVE_POSIX
	bool OpenNonBlocking(const char *pathname);

	static bool CreatePipe(FileDescriptor &r, FileDescriptor &w);

	/**
	 * Enable non-blocking mode on this file descriptor.
	 */
	void SetNonBlocking();

	/**
	 * Enable blocking mode on this file descriptor.
	 */
	void SetBlocking();

	/**
	 * Auto-close this file descriptor when a new program is
	 * executed.
	 */
	void EnableCloseOnExec();

	/**
	 * Do not auto-close this file descriptor when a new program
	 * is executed.
	 */
	void DisableCloseOnExec();

	/**
	 * Duplicate the file descriptor onto the given file descriptor.
	 */
	bool Duplicate(FileDescriptor new_fd) const {
		return ::dup2(Get(), new_fd.Get()) == 0;
	}

	/**
	 * Similar to Duplicate(), but if destination and source file
	 * descriptor are equal, clear the close-on-exec flag.  Use
	 * this method to inject file descriptors into a new child
	 * process, to be used by a newly executed program.
	 */
	bool CheckDuplicate(FileDescriptor new_fd);
#endif

#ifdef HAVE_EVENTFD
	bool CreateEventFD(unsigned initval=0);
#endif

#ifdef HAVE_SIGNALFD
	bool CreateSignalFD(const sigset_t *mask);
#endif

#ifdef HAVE_INOTIFY
	bool CreateInotify();
#endif

	/**
	 * Close the file descriptor.  It should not be called on an
	 * "undefined" object.  After this call, IsDefined() is guaranteed
	 * to return false, and this object may be reused.
	 */
	bool Close() {
		assert(IsDefined());

		return ::close(Steal()) == 0;
	}

	/**
	 * Rewind the pointer to the beginning of the file.
	 */
	bool Rewind();

	off_t Seek(off_t offset) {
		return lseek(Get(), offset, SEEK_SET);
	}

	off_t Skip(off_t offset) {
		return lseek(Get(), offset, SEEK_CUR);
	}

	gcc_pure
	off_t Tell() const {
		return lseek(Get(), 0, SEEK_CUR);
	}

	/**
	 * Returns the size of the file in bytes, or -1 on error.
	 */
	gcc_pure
	off_t GetSize() const;

	ssize_t Read(void *buffer, size_t length) {
		return ::read(fd, buffer, length);
	}

	ssize_t Write(const void *buffer, size_t length) {
		return ::write(fd, buffer, length);
	}

#ifdef HAVE_POSIX
	int Poll(short events, int timeout) const;

	int WaitReadable(int timeout) const;
	int WaitWritable(int timeout) const;

	gcc_pure
	bool IsReadyForWriting() const;
#endif
};

#endif
<|endoftext|>


<commit_before>#include "application.h"

// POSIX++
#include <cstring> // for strerror()
#include <cstdlib> // for exit

// STL
#include <atomic>
#include <list>

// PUT
#include <cxxutils/vterm.h>

// atomic vars are to avoid race conditions
static std::atomic_int  s_return_value (0);
static std::atomic_bool s_run (true); // quit signal

posix::lockable<std::queue<vfunc>> Application::ms_signal_queue;

static posix::fd_t s_pipeio[2] = { posix::invalid_descriptor }; //  execution stepper pipe

enum {
  Read = 0,
  Write = 1,
};

Application::Application(void) noexcept
{
  if(s_pipeio[Read] == posix::invalid_descriptor) // if execution stepper pipe  hasn't been initialized yet
  {
    flaw(!posix::pipe(s_pipeio),
         terminal::critical,
         std::exit(errno),,
         "Unable to create pipe for execution stepper: %s", std::strerror(errno))
    posix::fcntl(s_pipeio[Read], F_SETFD, FD_CLOEXEC); // close on exec*()
    posix::donotblock(s_pipeio[Read]); // don't block

    flaw(!EventBackend::add(s_pipeio[Read], EventBackend::SimplePollReadFlags, read),
         terminal::critical,
         std::exit(errno),, // watch for when execution stepper pipe has been triggered
         "Unable to watch execution stepper to backend: %s", std::strerror(errno))
  }
}

void Application::step(void) noexcept
{
  static const uint8_t dummydata = 0; // dummy content
  flaw(posix::write(s_pipeio[Write], &dummydata, 1) != 1,
       terminal::critical,
       std::exit(errno),, // triggers execution stepper FD
       "Unable to trigger Object signal queue processor: %s", std::strerror(errno))
}

// this is the callback function for the signal queue
void Application::read(posix::fd_t fd, native_flags_t) noexcept
{
  uint64_t discard;
  while(posix::read(fd, &discard, sizeof(discard)) != posix::error_response);

  // execute queue of object signal calls
  static std::queue<vfunc> exec_queue;
  if(exec_queue.empty()) // if not currently executing (recursive or multithread exec() calls?)
  {
    ms_signal_queue.lock(); // get exclusive access (make thread-safe)
    exec_queue.swap(ms_signal_queue); // swap the queues
    ms_signal_queue.unlock(); // access is no longer needed

    while(!exec_queue.empty()) // while still have object signals to execute
    {
      exec_queue.front()(); // execute current object signal/callback
      exec_queue.pop(); // discard current object signal
    }
  }
}

int Application::exec(void) noexcept // non-static function to ensure an instance of Application exists
{
  while(s_run) // while not quitting
  {
    EventBackend::poll(); // get event queue results

    for(std::pair<posix::fd_t, native_flags_t>& pair : EventBackend::results) // process results
    {
      EventBackend::queue.lock(); // get exclusive access (make thread-safe)
      auto entries = EventBackend::queue.equal_range(pair.first); // get all the entries for that FD
      std::list<std::pair<posix::fd_t, EventBackend::callback_info_t>> exec_fds(entries.first, entries.second); // copy entries
      EventBackend::queue.unlock(); // access is no longer needed

      for(std::pair<posix::fd_t, EventBackend::callback_info_t>& entry : exec_fds) // for each FD
        if(entry.second.flags & pair.second) // check if there is a matching flag
          entry.second.function(pair.first, pair.second); // invoke the callback function with the FD and triggering Flag
    }
  }
  return s_return_value; // quit() has been called, return value specified
}

void Application::quit(int return_value) noexcept // soft application exit (allows event queues to complete)
{
  if(s_run) // if not already quitting
  {
    s_return_value = return_value; // set application return value
    s_run = false; // indicate program must quit
  }
}
<commit_msg>set run value upon exec()<commit_after>#include "application.h"

// POSIX++
#include <cstring> // for strerror()
#include <cstdlib> // for exit

// STL
#include <atomic>
#include <list>

// PUT
#include <cxxutils/vterm.h>

// atomic vars are to avoid race conditions
static std::atomic_int  s_return_value (0);
static std::atomic_bool s_run (true); // quit signal

posix::lockable<std::queue<vfunc>> Application::ms_signal_queue;

static posix::fd_t s_pipeio[2] = { posix::invalid_descriptor }; //  execution stepper pipe

enum {
  Read = 0,
  Write = 1,
};

Application::Application(void) noexcept
{
  if(s_pipeio[Read] == posix::invalid_descriptor) // if execution stepper pipe  hasn't been initialized yet
  {
    flaw(!posix::pipe(s_pipeio),
         terminal::critical,
         std::exit(errno),,
         "Unable to create pipe for execution stepper: %s", std::strerror(errno))
    posix::fcntl(s_pipeio[Read], F_SETFD, FD_CLOEXEC); // close on exec*()
    posix::donotblock(s_pipeio[Read]); // don't block

    flaw(!EventBackend::add(s_pipeio[Read], EventBackend::SimplePollReadFlags, read),
         terminal::critical,
         std::exit(errno),, // watch for when execution stepper pipe has been triggered
         "Unable to watch execution stepper to backend: %s", std::strerror(errno))
  }
}

void Application::step(void) noexcept
{
  static const uint8_t dummydata = 0; // dummy content
  flaw(posix::write(s_pipeio[Write], &dummydata, 1) != 1,
       terminal::critical,
       std::exit(errno),, // triggers execution stepper FD
       "Unable to trigger Object signal queue processor: %s", std::strerror(errno))
}

// this is the callback function for the signal queue
void Application::read(posix::fd_t fd, native_flags_t) noexcept
{
  uint64_t discard;
  while(posix::read(fd, &discard, sizeof(discard)) != posix::error_response);

  // execute queue of object signal calls
  static std::queue<vfunc> exec_queue;
  if(exec_queue.empty()) // if not currently executing (recursive or multithread exec() calls?)
  {
    ms_signal_queue.lock(); // get exclusive access (make thread-safe)
    exec_queue.swap(ms_signal_queue); // swap the queues
    ms_signal_queue.unlock(); // access is no longer needed

    while(!exec_queue.empty()) // while still have object signals to execute
    {
      exec_queue.front()(); // execute current object signal/callback
      exec_queue.pop(); // discard current object signal
    }
  }
}

int Application::exec(void) noexcept // non-static function to ensure an instance of Application exists
{
  s_run = true;
  while(s_run) // while not quitting
  {
    EventBackend::poll(); // get event queue results

    for(std::pair<posix::fd_t, native_flags_t>& pair : EventBackend::results) // process results
    {
      EventBackend::queue.lock(); // get exclusive access (make thread-safe)
      auto entries = EventBackend::queue.equal_range(pair.first); // get all the entries for that FD
      std::list<std::pair<posix::fd_t, EventBackend::callback_info_t>> exec_fds(entries.first, entries.second); // copy entries
      EventBackend::queue.unlock(); // access is no longer needed

      for(std::pair<posix::fd_t, EventBackend::callback_info_t>& entry : exec_fds) // for each FD
        if(entry.second.flags & pair.second) // check if there is a matching flag
          entry.second.function(pair.first, pair.second); // invoke the callback function with the FD and triggering Flag
    }
  }
  return s_return_value; // quit() has been called, return value specified
}

void Application::quit(int return_value) noexcept // soft application exit (allows event queues to complete)
{
  if(s_run) // if not already quitting
  {
    s_return_value = return_value; // set application return value
    s_run = false; // indicate program must quit
  }
}
<|endoftext|>


<commit_before>#include "diskinfo.hpp"

#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <set>

#include "logger.hpp"
#include "config/cmd_args.hpp"

const char *partitions_path = "/proc/partitions";
const char *hdparm_path = "/sbin/hdparm";

struct partition_info_t {
    partition_info_t();
    partition_info_t(int nmajor, int nminor, int nblocks, std::string name);
    int nmajor;
    int nminor;
    int nblocks;
    std::string name;
};

void get_partition_map(std::vector<partition_info_t> &partitions);

partition_info_t::partition_info_t(int nmajor, int nminor, int nblocks, std::string name)
    : nmajor(nmajor), nminor(nminor), nblocks(nblocks), name(name) { }

partition_info_t::partition_info_t() {}

void tokenize_line(const std::string& line, std::vector<std::string> &tokens) {
    /* Given a line and an empty vector to fill in, chops the line up by space
     * characters and puts the words in the tokens vector.
     */
    size_t start, end = 0;
    while (end < line.size()) {
        start = end;
        while (start < line.size() && line[start] == ' ') {
            start++;
        }
        end = start;
        while (end < line.size() && line[end] != ' ') {
            end++;
        }
        if (end - start > 0) {
            tokens.push_back(std::string(line, start, end - start));
        }
    }
}

void get_partition_map(std::vector<partition_info_t *> &partitions) {
    /* /proc partitions looks like this:
     * major minor  #blocks  name
     *
     *   8        0 234234234 sda
     *   8        1 123948586 sda1
     *   ...
     *
     * The major and minor device numbers are hexadecimal numbers.
     * To parse we skip the first two lines and then chop up each line into
     * partition_info_t structs.
     *
     */
    std::ifstream partitions_file (partitions_path);
    std::string line;
    std::vector<std::string> tokens;
    char *endptr;
    if (partitions_file.is_open()) {
        getline(partitions_file, line);
        getline(partitions_file, line);
        while (partitions_file.good()) {
            getline(partitions_file, line);
            tokens.clear();
            tokenize_line(line, tokens);
            if (tokens.size() != 4) {
                continue;
            }

            partition_info_t *partition = new partition_info_t;
            partition->nmajor = strtol(tokens[0].c_str(), &endptr, 16);
            partition->nminor = strtol(tokens[1].c_str(), &endptr, 16);
            partition->nblocks = strtol(tokens[2].c_str(), &endptr, 10);
            partition->name = tokens[3];
            partition->name.insert(0, std::string("/dev/"));

            partitions.push_back(partition);
        }
    }
}

void log_disk_info(std::vector<log_serializer_private_dynamic_config_t> &serializers) {
    std::vector<partition_info_t *> partitions;
    std::set<std::string> devices;

    logINF("Disk info for database disks:\n");

    struct stat st;

    if (stat(hdparm_path, &st)) {
        logWRN("System lacks hdparm; giving up\n");
        return;
    }

    get_partition_map(partitions);

    int maj, min = 0;
    const char *path;
    mlog_start(INF);
    for (unsigned int i = 0; i < serializers.size(); i++) {
        path = serializers[i].db_filename.c_str();
        if (stat(path, &st)) {
            mlogf("Cannot stat \"%s\": %s\n", path, strerror(errno));
            continue;
        }

        maj = major(st.st_dev);
        min = minor(st.st_dev);
        if (maj == 0) {
            /* We're looking at a device, not a file */
            if (strncmp("/dev/ram", path, 8)) {
                devices.insert(path);
            }
            else {
                /* Don't give ramdisks to hdparm because it won't produce
                 * anything useful. */
                mlogf("\n%s:\n    RAM disk\n", path);
            }
            continue;
        }

        for (unsigned int j = 0; j < partitions.size(); j++) {
            if (partitions[j]->nmajor == maj && partitions[j]->nminor == min) {
                devices.insert(partitions[j]->name);
            }
        }
    }
    for (unsigned int i = 0; i < partitions.size(); i++) {
        delete partitions[i];
    }
    partitions.clear();

    if (devices.size() < 1) {
        /* Don't run hdparm unless we have devices to run it on. */
        mlog_end();
        return;
    }

    std::string cmd;
    for (std::set<std::string>::iterator it = devices.begin(); it != devices.end(); it++) {
        cmd.append(std::string(hdparm_path));
        cmd.append(std::string(" -iI"));
        cmd.append(*it);
        /* Standard error from hdparm isn't useful. */
        cmd.append(std::string(" 2>/dev/null; "));
    }

    char *buf = (char *) calloc(1024, sizeof(char));
    FILE *stream;
    size_t total_bytes = 0;
    size_t nbytes = 0;

    if (NULL == buf) {
        crash("out of memory\n");
    }
    if (NULL == (stream = popen(cmd.c_str(), "r"))) {
        crash("popen failed\n");
    }

    while (1023 == (nbytes = fread(buf, 1, 1023, stream)))
    {
        total_bytes += nbytes;
        mlogf("%s", buf);
    }
    if (-1 == pclose(stream)) {
        mlog_end();
        crash("pclose failed\n");
    }
    total_bytes += nbytes;
    buf[nbytes + 1] = '\0';
    if (total_bytes < 5) {
        /* No output. We didn't have enough permissions to run hdparm.*/
        mlogf("Must be root or part of group `disk' to get disk info.\n");
    }
    else {
        mlogf("%s\n", buf);
    }
    mlog_end();

    free(buf);
}
<commit_msg>Handle disks that don't support info querying<commit_after>#include "diskinfo.hpp"

#include <sys/stat.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <set>

#include "logger.hpp"
#include "config/cmd_args.hpp"

const char *partitions_path = "/proc/partitions";
const char *hdparm_path = "/sbin/hdparm";

struct partition_info_t {
    partition_info_t();
    partition_info_t(int nmajor, int nminor, int nblocks, std::string name);
    int nmajor;
    int nminor;
    int nblocks;
    std::string name;
};

void get_partition_map(std::vector<partition_info_t> &partitions);

partition_info_t::partition_info_t(int nmajor, int nminor, int nblocks, std::string name)
    : nmajor(nmajor), nminor(nminor), nblocks(nblocks), name(name) { }

partition_info_t::partition_info_t() {}

void tokenize_line(const std::string& line, std::vector<std::string> &tokens) {
    /* Given a line and an empty vector to fill in, chops the line up by space
     * characters and puts the words in the tokens vector.
     */
    size_t start, end = 0;
    while (end < line.size()) {
        start = end;
        while (start < line.size() && line[start] == ' ') {
            start++;
        }
        end = start;
        while (end < line.size() && line[end] != ' ') {
            end++;
        }
        if (end - start > 0) {
            tokens.push_back(std::string(line, start, end - start));
        }
    }
}

void get_partition_map(std::vector<partition_info_t *> &partitions) {
    /* /proc partitions looks like this:
     * major minor  #blocks  name
     *
     *   8        0 234234234 sda
     *   8        1 123948586 sda1
     *   ...
     *
     * The major and minor device numbers are hexadecimal numbers.
     * To parse we skip the first two lines and then chop up each line into
     * partition_info_t structs.
     *
     */
    std::ifstream partitions_file (partitions_path);
    std::string line;
    std::vector<std::string> tokens;
    char *endptr;
    if (partitions_file.is_open()) {
        getline(partitions_file, line);
        getline(partitions_file, line);
        while (partitions_file.good()) {
            getline(partitions_file, line);
            tokens.clear();
            tokenize_line(line, tokens);
            if (tokens.size() != 4) {
                continue;
            }

            partition_info_t *partition = new partition_info_t;
            partition->nmajor = strtol(tokens[0].c_str(), &endptr, 16);
            partition->nminor = strtol(tokens[1].c_str(), &endptr, 16);
            partition->nblocks = strtol(tokens[2].c_str(), &endptr, 10);
            partition->name = tokens[3];
            partition->name.insert(0, std::string("/dev/"));

            partitions.push_back(partition);
        }
    }
}

void log_disk_info(std::vector<log_serializer_private_dynamic_config_t> &serializers) {
    std::vector<partition_info_t *> partitions;
    std::set<std::string> devices;

    logINF("Disk info for database disks:\n");

    struct stat st;

    if (stat(hdparm_path, &st)) {
        logWRN("System lacks hdparm; giving up\n");
        return;
    }

    get_partition_map(partitions);

    int maj, min = 0;
    const char *path;
    const char *dev_path;

    uid_t uid = geteuid();
    int device_idx;

    mlog_start(INF);
    for (unsigned int i = 0; i < serializers.size(); i++) {
        path = serializers[i].db_filename.c_str();
        if (stat(path, &st)) {
            mlogf("Cannot stat \"%s\": %s\n", path, strerror(errno));
            continue;
        }
        maj = major(st.st_dev);
        min = minor(st.st_dev);
        if (maj == 0) {
            /* We're looking at a device, not a file */
            if (strncmp("/dev/ram", path, 8)) {
                devices.insert(path);
            }
            else {
                /* Don't give ramdisks to hdparm because it won't produce
                 * anything useful. */
                mlogf("\n%s:\n    RAM disk\n", path);
            }
            continue;
        }

        device_idx = -1;
        for (unsigned int j = 0; j < partitions.size(); j++) {
            if (partitions[j]->nmajor == maj && partitions[j]->nminor == min) {
                device_idx = j;
                break;
            }
        }

        if (device_idx != -1) {
            dev_path = partitions[device_idx]->name.c_str();
            if (stat(dev_path, &st)) {
                mlogf("Cannot stat \"%s\": %s\n", dev_path, strerror(errno));
                continue;
            }
            if (S_ISDIR(st.st_mode) || S_ISFIFO(st.st_mode)) {
                mlogf("Invalid database file: \"%s\"\n", dev_path);
                continue;
            }
            if (-1 == access(dev_path, R_OK | W_OK) && 0 != uid) {
                mlogf("Must be root or have write permissions on \"%s\" to get disk info.\n", dev_path);
                continue;
            }

            devices.insert(partitions[device_idx]->name);
        }

    }
    for (unsigned int i = 0; i < partitions.size(); i++) {
        delete partitions[i];
    }
    partitions.clear();

    if (devices.size() < 1) {
        /* Don't run hdparm unless we have devices to run it on. */
        mlog_end();
        return;
    }

    std::string cmd;
    for (std::set<std::string>::iterator it = devices.begin(); it != devices.end(); it++) {
        cmd.append(std::string(hdparm_path));
        cmd.append(std::string(" -iI "));
        cmd.append(*it);
        /* Standard error from hdparm isn't useful. */
        cmd.append(std::string(" 2>/dev/null; "));
    }

    printf("%s\n", cmd.c_str());

    char *buf = (char *) calloc(1024, sizeof(char));
    FILE *stream;
    size_t total_bytes = 0;
    size_t nbytes = 0;

    if (NULL == buf) {
        crash("out of memory\n");
    }
    if (NULL == (stream = popen(cmd.c_str(), "r"))) {
        crash("popen failed\n");
    }

    while (1023 == (nbytes = fread(buf, 1, 1023, stream)))
    {
        total_bytes += nbytes;
        mlogf("%s", buf);
    }
    if (-1 == pclose(stream)) {
        mlog_end();
        crash("pclose failed\n");
    }
    total_bytes += nbytes;
    buf[nbytes] = '\0';
    if (total_bytes < 20 * devices.size()) {
        /* No output. We didn't have enough permissions to run hdparm.*/
        mlogf("None of the supplied disks support disk info queries.\n");
    }
    else {
        mlogf("%s\n", buf);
    }
    mlog_end();

    free(buf);
}
<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <aidu_mobile_base/motor.h>
#include <threemxl/C3mxlROS.h>
#include <XMLConfiguration.h>
#include <string>

using namespace aidu;

mobile_base::Motor::Motor(std::string name, std::string motor_port_name, std::string motor_config_name, double radiusWheel) {

    // Load motor configuration
    CXMLConfiguration motor_config_xml;
    motor_config_xml.loadFile(motor_config_name);

    // ThreeMXL initialization
    // Create configuration
    config = new CDxlConfig();
    config->readConfig(motor_config_xml.root().section(name.c_str()));
    this->name = name;

    // Create and configure 3mxl motor
    motor = new C3mxlROS(motor_port_name.c_str());
    motor->setWheelDiameter(radiusWheel * 2.0);
    motor->setConfig(config);
    motor->init();
    motor->set3MxlMode(POSITION_MODE);
    currentVelocity = 0;

}

void mobile_base::Motor::setVelocity(float velocity) {
    if (fabs(velocity-currentVelocity) > 0.01) {
        currentVelocity = velocity;

        // Send velocity to the 3mxl board
        ROS_INFO("%s: Setting velocity to %f", name.c_str(), velocity);

        // Check mode of 3mxl
        motor->get3MxlMode();
        if(motor->present3MxlMode() != SPEED_MODE){
        motor->set3MxlMode(SPEED_MODE);
        }

        // Send speed to 3mxl
        motor->setSpeed(velocity);
    }
}

void mobile_base::Motor::setPosition(float position) {

    // Send velocity to the 3mxl board
    ROS_INFO("%s: Setting position to %f", name.c_str(), position);

    // Check mode of 3mxl
    motor->get3MxlMode();
    if(motor->present3MxlMode() != POSITION_MODE){
        motor->set3MxlMode(POSITION_MODE);
    }

    // Send position to 3mxl
    motor->setPos(position);
}

mobile_base::Motor::~Motor(){
    motor->setSpeed(0);
    serial_port->port_close();
    delete config;
    delete motor;
    delete serial_port;
}
<commit_msg>Placed wheel diamater setting of motor after the init<commit_after>#include <ros/ros.h>
#include <aidu_mobile_base/motor.h>
#include <threemxl/C3mxlROS.h>
#include <XMLConfiguration.h>
#include <string>

using namespace aidu;

mobile_base::Motor::Motor(std::string name, std::string motor_port_name, std::string motor_config_name, double radiusWheel) {

    // Load motor configuration
    CXMLConfiguration motor_config_xml;
    motor_config_xml.loadFile(motor_config_name);

    // ThreeMXL initialization
    // Create configuration
    config = new CDxlConfig();
    config->readConfig(motor_config_xml.root().section(name.c_str()));
    this->name = name;

    // Create and configure 3mxl motor
    motor = new C3mxlROS(motor_port_name.c_str());
    motor->setConfig(config);
    motor->init();
    motor->setWheelDiameter(radiusWheel * 2.0);
    motor->set3MxlMode(POSITION_MODE);
    currentVelocity = 0;

}

void mobile_base::Motor::setVelocity(float velocity) {
    if (fabs(velocity-currentVelocity) > 0.01) {
        currentVelocity = velocity;

        // Send velocity to the 3mxl board
        ROS_INFO("%s: Setting velocity to %f", name.c_str(), velocity);

        // Check mode of 3mxl
        motor->get3MxlMode();
        if(motor->present3MxlMode() != SPEED_MODE){
        motor->set3MxlMode(SPEED_MODE);
        }

        // Send speed to 3mxl
        motor->setSpeed(velocity);
    }
}

void mobile_base::Motor::setPosition(float position) {

    // Send velocity to the 3mxl board
    ROS_INFO("%s: Setting position to %f", name.c_str(), position);

    // Check mode of 3mxl
    motor->get3MxlMode();
    if(motor->present3MxlMode() != POSITION_MODE){
        motor->set3MxlMode(POSITION_MODE);
    }

    // Send position to 3mxl
    motor->setPos(position);
}

mobile_base::Motor::~Motor(){
    motor->setSpeed(0);
    serial_port->port_close();
    delete config;
    delete motor;
    delete serial_port;
}
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2012 J-P Nurmi <jpnurmi@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include "ircmessagedecoder_p.h"
#include "irccodecplugin.h"
#include <QCoreApplication>
#include <QPluginLoader>
#include <QStringList>
#include <QDebug>
#include <QMap>
#include <QDir>

extern "C" {
    int IsUTF8Text(const char* utf8, int len);
}

typedef QMap<QByteArray, IrcCodecPlugin*> IrcCodecPluginMap;
Q_GLOBAL_STATIC(IrcCodecPluginMap, irc_codec_plugins)

static QByteArray irc_plugin_key = qgetenv("COMMUNI_CODEC_PLUGIN");

COMMUNI_EXPORT void irc_set_codec_plugin(const QByteArray& key)
{
    irc_plugin_key = key;
}

IrcMessageDecoder::IrcMessageDecoder()
{
    d.fallback = QTextCodec::codecForName("ISO-8859-15");
}

IrcMessageDecoder::~IrcMessageDecoder()
{
}

QByteArray IrcMessageDecoder::encoding() const
{
    return d.fallback->name();
}

void IrcMessageDecoder::setEncoding(const QByteArray& encoding)
{
    if (QTextCodec::availableCodecs().contains(encoding))
        d.fallback = QTextCodec::codecForName(encoding);
}

QString IrcMessageDecoder::decode(const QByteArray& data) const
{
    // TODO: not thread safe
    static QByteArray pluginKey;
    static bool initialized = false;
    if (!initialized) {
        pluginKey = const_cast<IrcMessageDecoder*>(this)->initialize();
        initialized = true;
    }

    QTextCodec* codec = 0;
    if (IsUTF8Text(data, data.length())) {
        codec = QTextCodec::codecForName("UTF-8");
    } else {
        QByteArray name = d.fallback->name();
        IrcCodecPlugin* plugin = irc_codec_plugins()->value(pluginKey);
        if (plugin)
            name = plugin->codecForData(data);
        codec = QTextCodec::codecForName(name);
    }

    if (!codec)
        codec = d.fallback;
    Q_ASSERT(codec);
    return codec->toUnicode(data);
}

QByteArray IrcMessageDecoder::initialize()
{
    bool loaded = loadPlugins();
    QByteArray pluginKey = irc_plugin_key;
    if (!pluginKey.isEmpty() && !irc_codec_plugins()->contains(pluginKey)) {
        qWarning() << "IrcMessageDecoder:" << pluginKey << "plugin not loaded";
        if (loaded)
            qWarning() << "IrcMessageDecoder: available plugins:" << irc_codec_plugins()->keys();
    }
    if (!loaded)
        qWarning() << "IrcMessageDecoder: no plugins available";

    if (pluginKey.isEmpty() && !irc_codec_plugins()->isEmpty())
        pluginKey = irc_codec_plugins()->keys().first();
    return pluginKey;
}

#if defined(Q_OS_WIN)
#define COMMUNI_PATH_SEPARATOR ';'
#else
#define COMMUNI_PATH_SEPARATOR ':'
#endif

static QStringList pluginPaths()
{
    QStringList paths = QCoreApplication::libraryPaths();
    const QByteArray env = qgetenv("COMMUNI_PLUGIN_PATH");
    if (!env.isEmpty()) {
        foreach(const QString & path, QFile::decodeName(env).split(COMMUNI_PATH_SEPARATOR, QString::SkipEmptyParts)) {
            QString canonicalPath = QDir(path).canonicalPath();
            if (!canonicalPath.isEmpty() && !paths.contains(canonicalPath))
                paths += canonicalPath;
        }
    }
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcMessageDecoder: dynamic plugin paths:" << paths;
    return paths;
}

bool IrcMessageDecoder::loadPlugins()
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    foreach(QObject* instance, QPluginLoader::staticInstances()) {
        IrcCodecPlugin* plugin = qobject_cast<IrcCodecPlugin*>(instance);
        if (plugin) {
            irc_codec_plugins()->insert(plugin->key(), plugin);
            if (dbg) qDebug() << "IrcMessageDecoder: loaded static plugin:" << plugin->key();
        }
    }

    foreach(const QString & path, pluginPaths()) {
        QDir dir(path);
        if (!dir.cd("communi"))
            continue;

        foreach(const QFileInfo & file, dir.entryInfoList(QDir::Files)) {
            QPluginLoader loader(file.absoluteFilePath());
            IrcCodecPlugin* plugin = qobject_cast<IrcCodecPlugin*>(loader.instance());
            if (plugin) {
                irc_codec_plugins()->insert(plugin->key(), plugin);
                if (dbg) qDebug() << "IrcMessageDecoder: loaded dynamic plugin:" << file.fileName();
            }
        }
    }
    return !irc_codec_plugins()->isEmpty();
}
<commit_msg>IrcMessageDecoder: pick the last plugin by default<commit_after>/*
* Copyright (C) 2008-2012 J-P Nurmi <jpnurmi@gmail.com>
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
* License for more details.
*/

#include "ircmessagedecoder_p.h"
#include "irccodecplugin.h"
#include <QCoreApplication>
#include <QPluginLoader>
#include <QStringList>
#include <QDebug>
#include <QMap>
#include <QDir>

extern "C" {
    int IsUTF8Text(const char* utf8, int len);
}

typedef QMap<QByteArray, IrcCodecPlugin*> IrcCodecPluginMap;
Q_GLOBAL_STATIC(IrcCodecPluginMap, irc_codec_plugins)

static QByteArray irc_plugin_key = qgetenv("COMMUNI_CODEC_PLUGIN");

COMMUNI_EXPORT void irc_set_codec_plugin(const QByteArray& key)
{
    irc_plugin_key = key;
}

IrcMessageDecoder::IrcMessageDecoder()
{
    d.fallback = QTextCodec::codecForName("ISO-8859-15");
}

IrcMessageDecoder::~IrcMessageDecoder()
{
}

QByteArray IrcMessageDecoder::encoding() const
{
    return d.fallback->name();
}

void IrcMessageDecoder::setEncoding(const QByteArray& encoding)
{
    if (QTextCodec::availableCodecs().contains(encoding))
        d.fallback = QTextCodec::codecForName(encoding);
}

QString IrcMessageDecoder::decode(const QByteArray& data) const
{
    // TODO: not thread safe
    static QByteArray pluginKey;
    static bool initialized = false;
    if (!initialized) {
        pluginKey = const_cast<IrcMessageDecoder*>(this)->initialize();
        initialized = true;
    }

    QTextCodec* codec = 0;
    if (IsUTF8Text(data, data.length())) {
        codec = QTextCodec::codecForName("UTF-8");
    } else {
        QByteArray name = d.fallback->name();
        IrcCodecPlugin* plugin = irc_codec_plugins()->value(pluginKey);
        if (plugin)
            name = plugin->codecForData(data);
        codec = QTextCodec::codecForName(name);
    }

    if (!codec)
        codec = d.fallback;
    Q_ASSERT(codec);
    return codec->toUnicode(data);
}

QByteArray IrcMessageDecoder::initialize()
{
    bool loaded = loadPlugins();
    QByteArray pluginKey = irc_plugin_key;
    if (!pluginKey.isEmpty() && !irc_codec_plugins()->contains(pluginKey)) {
        qWarning() << "IrcMessageDecoder:" << pluginKey << "plugin not loaded";
        if (loaded)
            qWarning() << "IrcMessageDecoder: available plugins:" << irc_codec_plugins()->keys();
    }
    if (!loaded)
        qWarning() << "IrcMessageDecoder: no plugins available";

    if (pluginKey.isEmpty() && !irc_codec_plugins()->isEmpty())
        pluginKey = irc_codec_plugins()->keys().last();
    return pluginKey;
}

#if defined(Q_OS_WIN)
#define COMMUNI_PATH_SEPARATOR ';'
#else
#define COMMUNI_PATH_SEPARATOR ':'
#endif

static QStringList pluginPaths()
{
    QStringList paths = QCoreApplication::libraryPaths();
    const QByteArray env = qgetenv("COMMUNI_PLUGIN_PATH");
    if (!env.isEmpty()) {
        foreach(const QString & path, QFile::decodeName(env).split(COMMUNI_PATH_SEPARATOR, QString::SkipEmptyParts)) {
            QString canonicalPath = QDir(path).canonicalPath();
            if (!canonicalPath.isEmpty() && !paths.contains(canonicalPath))
                paths += canonicalPath;
        }
    }
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcMessageDecoder: dynamic plugin paths:" << paths;
    return paths;
}

bool IrcMessageDecoder::loadPlugins()
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    foreach(QObject* instance, QPluginLoader::staticInstances()) {
        IrcCodecPlugin* plugin = qobject_cast<IrcCodecPlugin*>(instance);
        if (plugin) {
            irc_codec_plugins()->insert(plugin->key(), plugin);
            if (dbg) qDebug() << "IrcMessageDecoder: loaded static plugin:" << plugin->key();
        }
    }

    foreach(const QString & path, pluginPaths()) {
        QDir dir(path);
        if (!dir.cd("communi"))
            continue;

        foreach(const QFileInfo & file, dir.entryInfoList(QDir::Files)) {
            QPluginLoader loader(file.absoluteFilePath());
            IrcCodecPlugin* plugin = qobject_cast<IrcCodecPlugin*>(loader.instance());
            if (plugin) {
                irc_codec_plugins()->insert(plugin->key(), plugin);
                if (dbg) qDebug() << "IrcMessageDecoder: loaded dynamic plugin:" << file.fileName();
            }
        }
    }
    return !irc_codec_plugins()->isEmpty();
}
<|endoftext|>


<commit_before><commit_msg>Let's check the validity of sheet indices.  You never know...<commit_after><|endoftext|>