blob: ca88f892ed9787001da3412767160358f330de5e [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef P2P_COMMON_STRUCT_SERIALIZER_H__
#define P2P_COMMON_STRUCT_SERIALIZER_H__
#include <glib.h>
#include <unistd.h>
#include <base/logging.h>
namespace p2p {
namespace util {
// StructSerializerWrite writes the passed struct T |data| to a file descriptor
// that should be consumed by a StructSerializerWatcher in the other end.
template<typename T>
bool StructSerializerWrite(int fd, const T& data) {
const char* p = reinterpret_cast<const char*>(&data);
size_t to_write = sizeof(T);
int res;
do {
res = write(fd, p, to_write);
if (res < 0 && errno == EAGAIN)
continue;
if (res <= 0) {
PLOG(ERROR) << "Error writing to fd " << fd;
return false;
}
to_write -= res;
p += res;
} while (to_write > 0);
return true;
}
// StructSerializerWarcher adds a new watch to the glib's main loop that reads
// "typename T" objects and fires the provided callback each time such object
// is received. "typename T" is required to have a fixed size and the memory
// representing it is copied from StructSerializerWrite to
// StructSerializerWatcher without any conversion, thus only suitable for
// structs of basic types and fixed length arrays of those.
template<typename T>
class StructSerializerWatcher {
public:
// The callback function type that will be used for a StructSerializerWatcher
// of the struct T.
typedef void StructSerializerCallback(const T& data, void* user_data);
StructSerializerWatcher(
int fd,
StructSerializerCallback* callback,
void* user_data)
: source_id_(0),
fd_(fd),
callback_(callback),
user_data_(user_data),
buffer_len_(0) {
GError* error = NULL;
GIOChannel* io_channel = g_io_channel_unix_new(fd);
if (g_io_channel_set_encoding(io_channel, NULL, &error) ==
G_IO_STATUS_ERROR) {
LOG(ERROR) << "Setting NULL encoding: " << error->message;
g_error_free(error);
}
g_io_channel_set_buffered(io_channel, FALSE);
source_id_ = g_io_add_watch(
io_channel,
static_cast<GIOCondition>(G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP),
OnIOChannelActivity,
this);
g_io_channel_unref(io_channel);
}
~StructSerializerWatcher() {
if (source_id_)
g_source_remove(source_id_);
}
private:
static gboolean OnIOChannelActivity(GIOChannel* source,
GIOCondition condition,
gpointer user_data) {
StructSerializerWatcher<T>* watcher =
reinterpret_cast<StructSerializerWatcher<T>*>(user_data);
int bytes_read = 0;
CHECK(watcher->buffer_len_ < watcher->struct_size_);
char* char_buffer = reinterpret_cast<char*>(&watcher->buffer_);
do {
bytes_read = read(watcher->fd_,
char_buffer + watcher->buffer_len_,
struct_size_ - watcher->buffer_len_);
} while (bytes_read < 0 && errno == EAGAIN);
if (bytes_read < 0) {
PLOG(ERROR) << "Error reading from pipe";
return TRUE;
}
if ((condition & G_IO_HUP) != 0 || bytes_read == 0) {
watcher->source_id_ = 0;
return FALSE; // Stop monitoring the file.
}
watcher->buffer_len_ += bytes_read;
if (watcher->buffer_len_ == watcher->struct_size_) {
(*watcher->callback_)(watcher->buffer_, watcher->user_data_);
watcher->buffer_len_ = 0;
}
return TRUE; // Keep source around.
}
// The source id used to track the callback source on the main loop.
guint source_id_;
// The passed file descriptor.
int fd_;
StructSerializerCallback* callback_;
// A user provided pointer passed back to callback on every call.
void* user_data_;
static const size_t struct_size_ = sizeof(T);
// The buffer to store partial reads from the passed |fd_|.
T buffer_;
// The current number of bytes stored in |buffer_|.
size_t buffer_len_;
};
} // namespace util
} // namespace p2p
#endif // P2P_COMMON_STRUCT_SERIALIZER_H__