ccls/src/platform_linux.cc
Jacob Dufault 54fed027ce Fix CopyFileTo when there is already a destination file.
This fixes reindexing files, as before we would update the json but not
the indexed file contents which caused code lens and references to get
out of sync.
2017-05-19 17:41:27 -07:00

236 lines
5.9 KiB
C++

#if defined(__linux__) || defined(__APPLE__)
#include "platform.h"
#include "utils.h"
#include <cassert>
#include <string>
#include <pthread.h>
#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <dirent.h>
#include <sys/types.h> // required for stat.h
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
#include <sys/mman.h>
#ifndef __APPLE__
#include <sys/prctl.h>
#endif
struct PlatformMutexLinux : public PlatformMutex {
sem_t* sem_ = nullptr;
PlatformMutexLinux(const std::string& name) {
std::cerr << "PlatformMutexLinux name=" << name << std::endl;
sem_ = sem_open(name.c_str(), O_CREAT, 0666 /*permission*/,
1 /*initial_value*/);
}
~PlatformMutexLinux() override { sem_close(sem_); }
};
struct PlatformScopedMutexLockLinux : public PlatformScopedMutexLock {
sem_t* sem_ = nullptr;
PlatformScopedMutexLockLinux(sem_t* sem) : sem_(sem) { sem_wait(sem_); }
~PlatformScopedMutexLockLinux() override { sem_post(sem_); }
};
void* checked(void* result, const char* expr) {
if (!result) {
std::cerr << "FAIL errno=" << errno << " in |" << expr << "|" << std::endl;
std::cerr << "errno => " << strerror(errno) << std::endl;
exit(1);
}
return result;
}
int checked(int result, const char* expr) {
if (result == -1) {
std::cerr << "FAIL errno=" << errno << " in |" << expr << "|" << std::endl;
std::cerr << "errno => " << strerror(errno) << std::endl;
exit(1);
}
return result;
}
#define CHECKED(expr) checked(expr, #expr)
struct PlatformSharedMemoryLinux : public PlatformSharedMemory {
std::string name_;
size_t size_;
int fd_;
PlatformSharedMemoryLinux(const std::string& name, size_t size)
: name_(name), size_(size) {
std::cerr << "PlatformSharedMemoryLinux name=" << name << ", size=" << size << std::endl;
// Try to create shared memory but only if it does not already exist. Since
// we created the memory, we need to initialize it.
fd_ = shm_open(name_.c_str(), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
if (fd_ >= 0) {
std::cerr << "Calling ftruncate fd_=" << fd_ << std::endl;
CHECKED(ftruncate(fd_, size));
}
// Otherwise, we just open existing shared memory. We don't need to
// create or initialize it.
else {
fd_ = CHECKED(shm_open(
name_.c_str(), O_RDWR, /* memory is read/write, create if needed */
S_IRUSR | S_IWUSR /* user read/write */));
}
// Map the shared memory to an address.
data =
CHECKED(mmap(nullptr /*kernel assigned starting address*/, size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0 /*offset*/));
capacity = size;
std::cerr << "Open shared memory name=" << name << ", fd=" << fd_
<< ", shared=" << data << ", capacity=" << capacity << std::endl;
}
~PlatformSharedMemoryLinux() override {
CHECKED(munmap(data, size_));
CHECKED(shm_unlink(name_.c_str()));
data = nullptr;
}
};
std::unique_ptr<PlatformMutex> CreatePlatformMutex(const std::string& name) {
std::string name2 = "/" + name;
return MakeUnique<PlatformMutexLinux>(name2);
}
std::unique_ptr<PlatformScopedMutexLock> CreatePlatformScopedMutexLock(
PlatformMutex* mutex) {
return MakeUnique<PlatformScopedMutexLockLinux>(
static_cast<PlatformMutexLinux*>(mutex)->sem_);
}
std::unique_ptr<PlatformSharedMemory> CreatePlatformSharedMemory(
const std::string& name, size_t size) {
std::string name2 = "/" + name;
return MakeUnique<PlatformSharedMemoryLinux>(name2, size);
}
void PlatformInit() {
}
std::string NormalizePath(const std::string& path) {
errno = 0;
char name[PATH_MAX + 1];
realpath(path.c_str(), name);
if (errno)
return path;
return name;
}
bool TryMakeDirectory(const std::string& absolute_path) {
const mode_t kMode = 0777; // UNIX style permissions
if (mkdir(absolute_path.c_str(), kMode) == -1) {
// Success if the directory exists.
return errno == EEXIST;
}
return true;
}
void SetCurrentThreadName(const std::string& thread_name) {
#ifndef __APPLE__
prctl(PR_SET_NAME, thread_name.c_str(), 0, 0, 0);
#endif
}
int64_t GetLastModificationTime(const std::string& absolute_path) {
struct stat buf;
if (stat(absolute_path.c_str(), &buf) != 0) {
switch (errno) {
case ENOENT:
std::cerr << "GetLastModificationTime: unable to find file " << absolute_path << std::endl;
break;
case EINVAL:
std::cerr << "GetLastModificationTime: invalid param to _stat for file file " << absolute_path << std::endl;
break;
default:
std::cerr << "GetLastModificationTime: unhandled for " << absolute_path << std::endl;
exit(1);
break;
}
}
return buf.st_mtime;
}
// See http://stackoverflow.com/q/13198627
void CopyFileTo(const std::string& dest, const std::string& source) {
int fd_from = open(source.c_str(), O_RDONLY);
if (fd_from < 0)
return;
int fd_to = open(dest.c_str(), O_WRONLY | O_CREAT, 0666);
if (fd_to < 0)
goto out_error;
char buf[4096];
ssize_t nread;
while (nread = read(fd_from, buf, sizeof buf), nread > 0) {
char *out_ptr = buf;
ssize_t nwritten;
do {
nwritten = write(fd_to, out_ptr, nread);
if (nwritten >= 0) {
nread -= nwritten;
out_ptr += nwritten;
}
else if (errno != EINTR)
goto out_error;
} while (nread > 0);
}
if (nread == 0) {
if (close(fd_to) < 0) {
fd_to = -1;
goto out_error;
}
close(fd_from);
return;
}
out_error:
close(fd_from);
if (fd_to >= 0)
close(fd_to);
}
std::vector<std::string> GetPlatformClangArguments() {
return {};
}
#undef CHECKED
#endif