blob: 722073c14bc80118490383c8619e8fa3e7c99c88 [file] [log] [blame]
#include <ATen/MapAllocator.h>
#include <atomic>
#include <string>
#if ATOMIC_INT_LOCK_FREE == 2
#define AT_ATOMIC_IPC_REFCOUNT 1
#endif
#include <c10/core/CPUAllocator.h>
#include <c10/util/C++17.h>
#include <c10/util/Unicode.h>
/* stuff for mapped files */
#ifdef _WIN32
#include <c10/util/win32-headers.h>
#endif
#if defined(HAVE_MMAP)
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#if !defined(_MSC_VER) || defined(HAVE_MMAP)
#include <sys/types.h>
#include <unistd.h>
#elif defined(_MSC_VER)
#include <c10/util/win32-headers.h>
#endif
namespace at {
static constexpr int64_t map_alloc_alignment = 64;
TORCH_API std::string NewProcessWideShmHandle()
{
static std::atomic<uint64_t> counter;
std::string handle = "/torch_";
#ifdef _MSC_VER
handle += c10::guts::to_string(GetCurrentProcessId());
#else
handle += c10::guts::to_string(getpid());
#endif
handle += "_";
handle += c10::guts::to_string(counter.fetch_add(1, std::memory_order_relaxed));
return handle;
}
#if defined(_WIN32) || defined(HAVE_MMAP)
namespace {
struct MapInfo {
std::atomic<int> refcount;
};
const std::string unknown_filename = "filename not specified";
#ifdef _WIN32
const std::string unknown_eventname = "eventname not specified";
#endif
} // namespace (anonymous)
MapAllocator::MapAllocator(WithFd, std::string filename, int fd, int flags, size_t size)
: filename_(filename.empty() ? unknown_filename : std::move(filename))
, flags_(0) // to be filled later
, size_(0) // to be filled later
#ifdef _WIN32
, handle_(INVALID_HANDLE_VALUE) // to be filled later
, event_(INVALID_HANDLE_VALUE) // to be filled later
, eventname_(filename.empty() ? unknown_eventname : (filename + "_event"))
#else
, fd_(fd)
#endif
, base_ptr_(nullptr)
{
if (!(flags & ALLOCATOR_MAPPED_SHARED) && !(flags & ALLOCATOR_MAPPED_SHAREDMEM)) {
flags &= ~ALLOCATOR_MAPPED_NOCREATE;
}
if ((flags ^ ALLOCATOR_MAPPED_EXCLUSIVE) == 0) {
TORCH_CHECK(false, "ALLOCATOR_MAPPED_EXCLUSIVE flag requires opening the file in shared mode");
}
#ifdef _WIN32
if (fd != -1) {
TORCH_CHECK(false, "MapAllocator_newWithFd is unsupported on Windows");
}
#endif
flags_ = flags;
// OK, now do the allocation
if (size == 0) {
return;
}
#ifdef _WIN32
if (flags_ & ALLOCATOR_MAPPED_SHAREDMEM) {
// Shadowing
const wchar_t *filename;
const wchar_t *eventname;
const std::wstring wFilename = c10::u8u16(filename_);
const std::wstring wEventname = c10::u8u16(eventname_);
LARGE_INTEGER hfilesz;
if (filename_[0] == '/') {
filename = wFilename.c_str() + 1;
eventname = wEventname.c_str() + 1;
} else {
filename = wFilename.c_str();
eventname = wEventname.c_str();
}
hfilesz.QuadPart = size;
if (flags_ & ALLOCATOR_MAPPED_EXCLUSIVE) {
event_ = CreateEventW(nullptr, FALSE, FALSE, eventname);
} else if (flags_ & ALLOCATOR_MAPPED_NOCREATE) {
event_ = OpenEventW(EVENT_ALL_ACCESS, FALSE, eventname);
} else {
TORCH_CHECK(false, "Expected either ALLOCATOR_MAPPED_EXCLUSIVE or ALLOCATOR_MAPPED_NOCREATE");
}
if (event_ == nullptr) {
TORCH_CHECK(false, "Couldn't open shared event: <", eventname, ">, error code: <", GetLastError(), ">");
}
if (flags_ & ALLOCATOR_MAPPED_EXCLUSIVE) {
handle_ = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, hfilesz.HighPart, hfilesz.LowPart, filename);
} else if (flags_ & ALLOCATOR_MAPPED_NOCREATE) {
handle_ = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, filename);
} else {
TORCH_CHECK(false, "Expected either ALLOCATOR_MAPPED_EXCLUSIVE or ALLOCATOR_MAPPED_NOCREATE");
}
if (handle_ == nullptr) {
TORCH_CHECK(false, "Couldn't open shared file mapping: <", filename, ">, error code: <", GetLastError(), ">");
}
size_ = size;
base_ptr_ = MapViewOfFile(handle_, FILE_MAP_ALL_ACCESS, 0, 0, size);
if (!base_ptr_) {
TORCH_CHECK(false, "Couldn't map view of shared file <", filename, ">, error code: <", GetLastError(), ">");
}
} else {
HANDLE hfile;
HANDLE hmfile;
LARGE_INTEGER hfilesz;
if (flags_ & ALLOCATOR_MAPPED_EXCLUSIVE) {
TORCH_CHECK(false, "exclusive file mapping is not supported on Windows");
}
if (flags_ & ALLOCATOR_MAPPED_NOCREATE) {
TORCH_CHECK(false, "file mapping without creation is not supported on Windows");
}
if (flags_ & ALLOCATOR_MAPPED_KEEPFD) {
TORCH_CHECK(false, "ALLOCATOR_MAPPED_KEEPFD not supported on Windows");
}
if (flags_ & ALLOCATOR_MAPPED_FROMFD) {
TORCH_CHECK(false, "ALLOCATOR_MAPPED_FROMFD not supported on Windows");
}
// Shadowing
const wchar_t *filename;
const std::wstring wFilename = c10::u8u16(filename_);
filename = wFilename.c_str();
/* open file */
/* FILE_FLAG_RANDOM_ACCESS ? */
if (flags_) {
hfile = CreateFileW(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hfile == INVALID_HANDLE_VALUE) {
TORCH_CHECK(false, "could not open file <", filename_, "> in read-write mode; error code: <", GetLastError(), ">");
}
} else {
hfile = CreateFileW(filename, GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hfile == INVALID_HANDLE_VALUE) {
TORCH_CHECK(false, "could not open file <", filename_, "> in read-only mode; error code: <", GetLastError(), ">");
}
}
if (GetFileSizeEx(hfile, &hfilesz) == 0) {
TORCH_CHECK(false, "could not get file size: <", filename_, ">; error code: <", GetLastError(), ">");
}
if (size > 0) {
if (size > hfilesz.QuadPart) {
if (flags_) {
hfilesz.QuadPart = size;
if (SetFilePointerEx(hfile, hfilesz, NULL, FILE_BEGIN) == 0) {
CloseHandle(hfile);
TORCH_CHECK(false, "unable to stretch file <", filename_, "> to the right size; error code: <", GetLastError(), ">", filename_);
}
if (SetEndOfFile(hfile) == 0) {
CloseHandle(hfile);
TORCH_CHECK(false, "unable to write to file <", filename_, ">; error code: <", GetLastError(), ">");
}
} else {
CloseHandle(hfile);
TORCH_CHECK(false, "file <", filename_, "> size is smaller than the required mapping size <", size, ">; error code: <", GetLastError(), ">");
}
}
} else {
size = hfilesz.QuadPart;
}
size_ = size; /* if we are here, it must be the right size */
hfilesz.QuadPart = size_;
/* get map handle */
if (flags_) {
if ( (hmfile = CreateFileMappingW(hfile, NULL, PAGE_READWRITE, hfilesz.HighPart, hfilesz.LowPart, NULL)) == NULL ) {
TORCH_CHECK(false, "could not create a map on file <", filename_, ">; error code: <", GetLastError(), ">");
}
} else {
if ( (hmfile = CreateFileMappingW(hfile, NULL, PAGE_WRITECOPY, hfilesz.HighPart, hfilesz.LowPart, NULL)) == NULL ) {
TORCH_CHECK(false, "could not create a map on file <", filename_, ">; error code: <", GetLastError(), ">");
}
}
/* map the stuff */
if(flags_) {
base_ptr_ = MapViewOfFile(hmfile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
} else {
base_ptr_ = MapViewOfFile(hmfile, FILE_MAP_COPY, 0, 0, 0);
}
CloseHandle(hfile);
CloseHandle(hmfile);
}
#else /* _WIN32 */
{
/* open file */
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
int fd;
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
int flags; // shadow
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
struct stat file_stat;
if (flags_ & (ALLOCATOR_MAPPED_SHARED | ALLOCATOR_MAPPED_SHAREDMEM)) {
flags = O_RDWR | O_CREAT;
} else {
flags = O_RDONLY;
}
if (flags_ & ALLOCATOR_MAPPED_EXCLUSIVE) {
flags |= O_EXCL;
}
if (flags_ & ALLOCATOR_MAPPED_NOCREATE) {
flags &= ~O_CREAT;
}
if (!(flags_ & ALLOCATOR_MAPPED_FROMFD)) {
if (flags_ & ALLOCATOR_MAPPED_SHARED) {
if ((fd = open(filename_.c_str(), flags, (mode_t)0600)) == -1) {
TORCH_CHECK(false, "unable to open file <", filename_, "> in read-write mode");
}
} else if (flags_ & ALLOCATOR_MAPPED_SHAREDMEM) {
#ifdef HAVE_SHM_OPEN
if((fd = shm_open(filename_.c_str(), flags, (mode_t)0600)) == -1) {
TORCH_CHECK(false, "unable to open shared memory object <", filename_, "> in read-write mode");
}
#else
TORCH_CHECK(false, "unable to open file <", filename_, "> in sharedmem mode, shm_open unavailable on this platform");
#endif
} else {
if ((fd = open(filename_.c_str(), O_RDONLY)) == -1) {
TORCH_CHECK(false, "unable to open file <", filename_, "> in read-only mode");
}
}
} else {
fd = fd_;
}
if (fstat(fd, &file_stat) == -1) {
if (!(flags_ & ALLOCATOR_MAPPED_FROMFD)) {
::close(fd);
}
TORCH_CHECK(false, "unable to stat the file <", filename_, ">");
}
if (size > 0) {
if (static_cast<int64_t>(size) > file_stat.st_size) {
if (flags_) {
if (ftruncate(fd, size) == -1) {
TORCH_CHECK(false, "unable to resize file <", filename_, "> to the right size");
}
if (fstat(fd, &file_stat) == -1 || file_stat.st_size < static_cast<int64_t>(size)) {
::close(fd);
TORCH_CHECK(false, "unable to stretch file <", filename_, "> to the right size");
}
/* on macOS write returns with errno 45 (Opperation not supported) when used
* with a file descriptor obtained via shm_open
*/
#ifndef __APPLE__
if ((write(fd, "", 1)) != 1) /* note that the string "" contains the '\0' byte ... */ {
::close(fd);
TORCH_CHECK(false, "unable to write to file <", filename_, ">");
}
#endif
} else {
::close(fd);
TORCH_CHECK(false, "file <", filename_, "> size is smaller than the required mapping size <", size, ">");
}
}
} else {
size = file_stat.st_size;
}
size_ = size; /* if we are here, it must be the right size */
/* map it */
if (flags_ & (ALLOCATOR_MAPPED_SHARED | ALLOCATOR_MAPPED_SHAREDMEM)) {
base_ptr_ = mmap(nullptr, size_, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
} else {
base_ptr_ = mmap(nullptr, size_, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
}
if (base_ptr_ == MAP_FAILED) {
base_ptr_ = nullptr; /* let's be sure it is NULL */
TORCH_CHECK(false, "unable to mmap ", size_, " bytes from file <", filename_, ">: ", strerror(errno), " (", errno, ")");
}
if (flags_ & ALLOCATOR_MAPPED_KEEPFD) {
fd_ = fd;
} else {
if (::close(fd) == -1) {
TORCH_CHECK(false, "Error closing file <", filename_, ">");
}
fd_ = -1;
}
if (flags_ & ALLOCATOR_MAPPED_UNLINK) {
if (flags_ & ALLOCATOR_MAPPED_SHAREDMEM) {
#ifdef HAVE_SHM_UNLINK
if (shm_unlink(filename_.c_str()) == -1) {
TORCH_CHECK(false, "could not unlink the shared memory file ", filename_);
}
#else
TORCH_CHECK(false, "could not unlink the shared memory file ", filename_, ", shm_unlink not available on platform");
#endif
} else {
if (unlink(filename_.c_str()) == -1)
TORCH_CHECK(false, "could not unlink file %s", filename_);
}
}
if (base_ptr_ == MAP_FAILED) {
TORCH_CHECK(false, "$ Torch: unable to mmap memory: you tried to mmap ", size_/1073741824, " GB.");
}
}
#endif
c10::reportMemoryUsageToProfiler(base_ptr_, size_, 0, size_, c10::Device(c10::DeviceType::CPU));
}
MapAllocator::MapAllocator(std::string filename, int flags, size_t size)
: MapAllocator(WITH_FD, std::move(filename), -1, flags, size)
{}
#ifdef _WIN32
struct ReleaseContext {
HANDLE event;
HANDLE handle;
HANDLE wait;
};
static void CALLBACK WaitForReleaseHandle(PVOID lpParam, BOOLEAN TimerOrWaitFired)
{
if (lpParam) {
ReleaseContext *ctx = (ReleaseContext *)lpParam;
SetEvent(ctx->event);
CloseHandle(ctx->event);
CloseHandle(ctx->handle);
UnregisterWait(ctx->wait);
delete ctx;
}
}
#endif
void MapAllocator::close() {
if (closed_) {
return;
}
closed_ = true;
if (base_ptr_ == nullptr) {
return;
}
#ifdef _WIN32
if ((flags_ & ALLOCATOR_MAPPED_KEEPFD) || (flags_ & ALLOCATOR_MAPPED_SHAREDMEM))
CloseHandle(handle_);
if(UnmapViewOfFile(base_ptr_) == 0)
TORCH_CHECK(false, "could not unmap the shared memory file");
#else /* _WIN32 */
if (flags_ & ALLOCATOR_MAPPED_KEEPFD) {
if (::close(fd_) == -1) {
TORCH_CHECK(false, "could not close file descriptor ", fd_);
}
}
if (munmap(base_ptr_, size_)) {
TORCH_CHECK(false, "could not unmap the shared memory file");
}
if (!(flags_ & (ALLOCATOR_MAPPED_FROMFD | ALLOCATOR_MAPPED_UNLINK))) {
if (flags_ & ALLOCATOR_MAPPED_SHAREDMEM) {
#ifdef HAVE_SHM_UNLINK
if (shm_unlink(filename_.c_str()) == -1) {
TORCH_CHECK(false, "could not unlink the shared memory file ", filename_);
}
#else
TORCH_CHECK(false, "could not unlink the shared memory file ", filename_, ", shm_unlink not available on platform");
#endif
}
}
#endif /* _WIN32 */
}
#else /* defined(_WIN32) || defined(HAVE_MMAP) */
MapAllocator::MapAllocator(std::string filename, int flags, size_t size) {
TORCH_CHECK(false, "file mapping not supported on your system");
}
MapAllocator::MapAllocator(WithFd, std::string filename, int fd, int flags, size_t size) {
TORCH_CHECK(false, "file mapping not supported on your system");
}
void MapAllocator::close() { }
#endif
#if (defined(_WIN32) || defined(HAVE_MMAP)) && defined(AT_ATOMIC_IPC_REFCOUNT)
RefcountedMapAllocatorArgCheck::RefcountedMapAllocatorArgCheck(int flags) {
if (flags & ALLOCATOR_MAPPED_FROMFD) {
TORCH_CHECK(false, "RefcountedMapAllocator doesn't support ALLOCATOR_MAPPED_FROMFD flag");
}
if (flags & ALLOCATOR_MAPPED_KEEPFD) {
TORCH_CHECK(false, "RefcountedMapAllocator doesn't support ALLOCATOR_MAPPED_KEEPFD flag");
}
if (flags & ALLOCATOR_MAPPED_UNLINK) {
TORCH_CHECK(false, "RefcountedMapAllocator doesn't support ALLOCATOR_MAPPED_UNLINK flag");
}
if (!(flags & ALLOCATOR_MAPPED_SHAREDMEM)) {
TORCH_CHECK(false, "RefcountedMapAllocator requires ALLOCATOR_MAPPED_SHAREDMEM flag");
}
}
RefcountedMapAllocator::RefcountedMapAllocator(const char *filename, int flags, size_t size)
: RefcountedMapAllocatorArgCheck(flags)
, MapAllocator(filename, flags, size + map_alloc_alignment) {
initializeAlloc();
}
RefcountedMapAllocator::RefcountedMapAllocator(WithFd, const char *filename, int fd, int flags, size_t size)
: RefcountedMapAllocatorArgCheck(flags)
, MapAllocator(WITH_FD, filename, flags, fd, size + map_alloc_alignment) {
initializeAlloc();
}
void RefcountedMapAllocator::initializeAlloc() {
MapInfo *map_info = (MapInfo*)base_ptr_;
#ifdef _WIN32
ReleaseContext* r_ctx = new ReleaseContext;
r_ctx->handle = handle_;
r_ctx->event = event_;
r_ctx->wait = NULL;
BOOL can_wait = RegisterWaitForSingleObject(&r_ctx->wait, event_, WaitForReleaseHandle, (PVOID)r_ctx, INFINITE, WT_EXECUTEONLYONCE);
TORCH_CHECK(can_wait, "Couldn't register wait on event, error code: <", GetLastError(), ">");
#endif
if (flags_ & ALLOCATOR_MAPPED_EXCLUSIVE) {
new (&map_info->refcount) std::atomic<int>(1);
} else {
map_info->refcount++;
}
}
void RefcountedMapAllocator::close() {
if (closed_) {
return;
}
closed_ = true;
void* data = base_ptr_;
#ifdef _WIN32
MapInfo *info = (MapInfo*)data;
if (--info->refcount == 0) {
SetEvent(event_);
}
if(UnmapViewOfFile(data) == 0) {
TORCH_CHECK(false, "could not unmap the shared memory file");
}
#else /* _WIN32 */
MapInfo *info = (MapInfo*)(data);
if (--info->refcount == 0) {
#ifdef HAVE_SHM_UNLINK
if (shm_unlink(filename_.c_str()) == -1) {
TORCH_CHECK(false, "could not unlink the shared memory file ", filename_);
}
#else
TORCH_CHECK(false, "could not unlink the shared memory file ", filename_, ", shm_unlink not available on platform");
#endif /* HAVE_SHM_UNLINK */
}
if (munmap(info, size_)) {
TORCH_CHECK(false, "could not unmap the shared memory file ", filename_);
}
#endif /* _WIN32 */
}
void RefcountedMapAllocator::incref()
{
MapInfo *map_info = static_cast<MapInfo*>(base_ptr_);
++map_info->refcount;
}
int RefcountedMapAllocator::decref()
{
MapInfo *map_info = static_cast<MapInfo*>(base_ptr_);
return --map_info->refcount == 0;
}
#else
RefcountedMapAllocatorArgCheck::RefcountedMapAllocatorArgCheck(int flags) {}
RefcountedMapAllocator::RefcountedMapAllocator(const char *filename, int flags, size_t size)
: RefcountedMapAllocatorArgCheck(flags),
MapAllocator(filename, flags, size + map_alloc_alignment)
{
TORCH_CHECK(false, "refcounted file mapping not supported on your system");
}
RefcountedMapAllocator::RefcountedMapAllocator(WithFd, const char *filename, int fd, int flags, size_t size)
: RefcountedMapAllocatorArgCheck(flags),
MapAllocator(WITH_FD, filename, flags, fd, size + map_alloc_alignment)
{
TORCH_CHECK(false, "refcounted file mapping not supported on your system");
}
void RefcountedMapAllocator::initializeAlloc() {}
void RefcountedMapAllocator::close() {}
#endif
static void deleteMapAllocator(void* ptr) {
delete static_cast<MapAllocator*>(ptr);
}
static void deleteRefcountedMapAllocator(void* ptr) {
delete static_cast<RefcountedMapAllocator*>(ptr);
}
MapAllocator* MapAllocator::fromDataPtr(const at::DataPtr& dptr) {
return dptr.cast_context<MapAllocator>(&deleteMapAllocator);
}
RefcountedMapAllocator* RefcountedMapAllocator::fromDataPtr(const at::DataPtr& dptr) {
return dptr.cast_context<RefcountedMapAllocator>(&deleteRefcountedMapAllocator);
}
at::DataPtr MapAllocator::makeDataPtr(std::string filename, int flags, size_t size, size_t* actual_size_out) {
auto* context = new MapAllocator(std::move(filename), flags, size);
if (actual_size_out) *actual_size_out = context->size();
return {context->data(), context, &deleteMapAllocator, at::DeviceType::CPU};
}
at::DataPtr MapAllocator::makeDataPtr(WithFd, const char *filename, int fd, int flags, size_t size, size_t* actual_size_out) {
auto* context = new MapAllocator(WITH_FD, filename, fd, flags, size);
if (actual_size_out) *actual_size_out = context->size();
return {context->data(), context, &deleteMapAllocator, at::DeviceType::CPU};
}
at::DataPtr RefcountedMapAllocator::makeDataPtr(const char *filename, int flags, size_t size, size_t* actual_size_out) {
auto* context = new RefcountedMapAllocator(filename, flags, size);
if (actual_size_out) *actual_size_out = context->size() - map_alloc_alignment;
return {context->data(), context, &deleteRefcountedMapAllocator, at::DeviceType::CPU};
}
at::DataPtr RefcountedMapAllocator::makeDataPtr(WithFd, const char *filename, int fd, int flags, size_t size, size_t* actual_size_out) {
auto* context = new RefcountedMapAllocator(WITH_FD, filename, fd, flags, size);
if (actual_size_out) *actual_size_out = context->size() - map_alloc_alignment;
return {context->data(), context, &deleteRefcountedMapAllocator, at::DeviceType::CPU};
}
void* RefcountedMapAllocator::data() const {
return static_cast<void*>(static_cast<char*>(base_ptr_) + map_alloc_alignment);
}
MapAllocator::~MapAllocator() {
// NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
close();
c10::reportMemoryUsageToProfiler(base_ptr_, -size_, 0, 0, c10::Device(c10::DeviceType::CPU));
}
} // namespace at