2017-04-20 18:38:20 +00:00
//* Copyright 2017 The NXT Authors
//*
//* 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.
2017-07-06 18:41:13 +00:00
# include "wire/Wire.h"
# include "wire/WireCmd.h"
2017-04-20 18:38:20 +00:00
2017-07-10 17:46:05 +00:00
# include "common/Assert.h"
2017-04-20 18:38:20 +00:00
2017-07-10 17:46:05 +00:00
# include <cstring>
2017-07-27 23:43:43 +00:00
# include <cstdlib>
2017-06-09 14:51:55 +00:00
# include <map>
2017-07-10 17:46:05 +00:00
# include <memory>
2017-07-27 23:43:43 +00:00
# include <string>
2017-07-10 17:46:05 +00:00
# include <vector>
2017-04-20 18:43:11 +00:00
2017-04-20 18:38:20 +00:00
namespace nxt {
namespace wire {
//* Client side implementation of the API, will serialize everything to memory to send to the server side.
namespace client {
class Device ;
2017-04-20 18:43:11 +00:00
struct BuilderCallbackData {
2017-07-27 23:43:43 +00:00
bool Call ( nxtBuilderErrorStatus status , const char * message ) {
2017-04-20 18:43:11 +00:00
if ( canCall & & callback ! = nullptr ) {
canCall = true ;
callback ( status , message , userdata1 , userdata2 ) ;
2017-07-27 23:43:43 +00:00
return true ;
2017-04-20 18:43:11 +00:00
}
2017-07-27 23:43:43 +00:00
return false ;
2017-04-20 18:43:11 +00:00
}
2017-05-10 13:07:36 +00:00
//* For help with development, prints all builder errors by default.
2017-07-27 23:43:43 +00:00
nxtBuilderErrorCallback callback = nullptr ;
2017-04-20 18:43:11 +00:00
nxtCallbackUserdata userdata1 = 0 ;
nxtCallbackUserdata userdata2 = 0 ;
bool canCall = true ;
} ;
2017-04-20 18:38:20 +00:00
//* All non-Device objects of the client side have:
//* - A pointer to the device to get where to serialize commands
//* - The external reference count
//* - An ID that is used to refer to this object when talking with the server side
struct ObjectBase {
ObjectBase ( Device * device , uint32_t refcount , uint32_t id )
: device ( device ) , refcount ( refcount ) , id ( id ) {
}
Device * device ;
uint32_t refcount ;
uint32_t id ;
2017-04-20 18:43:11 +00:00
BuilderCallbackData builderCallback ;
2017-04-20 18:38:20 +00:00
} ;
2017-06-09 14:51:55 +00:00
{ % set special_objects = [
" device " ,
" buffer " ,
] % }
{ % for type in by_category [ " object " ] if not type . name . canonical_case ( ) in special_objects % }
2017-04-20 18:38:20 +00:00
struct { { type . name . CamelCase ( ) } } : ObjectBase {
using ObjectBase : : ObjectBase ;
} ;
{ % endfor % }
2017-06-09 14:51:55 +00:00
struct Buffer : ObjectBase {
using ObjectBase : : ObjectBase ;
~ Buffer ( ) {
//* Callbacks need to be fired in all cases, as they can handle freeing resources
//* so we call them with "Unknown" status.
2018-03-20 23:48:54 +00:00
ClearMapRequests ( NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN ) ;
2017-06-09 14:51:55 +00:00
if ( mappedData ) {
free ( mappedData ) ;
}
}
2018-03-20 23:48:54 +00:00
void ClearMapRequests ( nxtBufferMapAsyncStatus status ) {
2017-06-09 14:51:55 +00:00
for ( auto & it : readRequests ) {
it . second . callback ( status , nullptr , it . second . userdata ) ;
}
readRequests . clear ( ) ;
}
//* We want to defer all the validation to the server, which means we could have multiple
//* map request in flight at a single time and need to track them separately.
//* On well-behaved applications, only one request should exist at a single time.
struct MapReadRequestData {
nxtBufferMapReadCallback callback = nullptr ;
nxtCallbackUserdata userdata = 0 ;
uint32_t size = 0 ;
} ;
std : : map < uint32_t , MapReadRequestData > readRequests ;
uint32_t readRequestSerial = 0 ;
//* Only one mapped pointer can be active at a time because Unmap clears all the in-flight requests.
void * mappedData = nullptr ;
} ;
2017-04-20 18:43:11 +00:00
//* TODO(cwallez@chromium.org): Do something with objects before they are destroyed ?
//* - Call still uncalled builder callbacks
2017-04-20 18:38:20 +00:00
template < typename T >
class ObjectAllocator {
public :
2017-04-20 18:43:11 +00:00
struct ObjectAndSerial {
ObjectAndSerial ( std : : unique_ptr < T > object , uint32_t serial )
: object ( std : : move ( object ) ) , serial ( serial ) {
}
std : : unique_ptr < T > object ;
uint32_t serial ;
} ;
2017-11-23 21:04:26 +00:00
ObjectAllocator ( Device * device ) : mDevice ( device ) {
2017-04-20 18:43:11 +00:00
// ID 0 is nullptr
2017-11-23 21:04:26 +00:00
mObjects . emplace_back ( nullptr , 0 ) ;
2017-04-20 18:38:20 +00:00
}
2017-04-20 18:43:11 +00:00
ObjectAndSerial * New ( ) {
uint32_t id = GetNewId ( ) ;
2017-11-23 21:04:26 +00:00
T * result = new T ( mDevice , 1 , id ) ;
2017-04-20 18:43:11 +00:00
auto object = std : : unique_ptr < T > ( result ) ;
2017-11-23 21:04:26 +00:00
if ( id > = mObjects . size ( ) ) {
ASSERT ( id = = mObjects . size ( ) ) ;
mObjects . emplace_back ( std : : move ( object ) , 0 ) ;
2017-04-20 18:43:11 +00:00
} else {
2017-11-23 21:04:26 +00:00
ASSERT ( mObjects [ id ] . object = = nullptr ) ;
2017-04-20 18:43:11 +00:00
//* TODO(cwallez@chromium.org): investigate if overflows could cause bad things to happen
2017-11-23 21:04:26 +00:00
mObjects [ id ] . serial + + ;
mObjects [ id ] . object = std : : move ( object ) ;
2017-04-20 18:43:11 +00:00
}
2017-11-23 21:04:26 +00:00
return & mObjects [ id ] ;
2017-04-20 18:38:20 +00:00
}
void Free ( T * obj ) {
FreeId ( obj - > id ) ;
2017-11-23 21:04:26 +00:00
mObjects [ obj - > id ] . object = nullptr ;
2017-04-20 18:43:11 +00:00
}
T * GetObject ( uint32_t id ) {
2017-11-23 21:04:26 +00:00
if ( id > = mObjects . size ( ) ) {
2017-04-20 18:43:11 +00:00
return nullptr ;
}
2017-11-23 21:04:26 +00:00
return mObjects [ id ] . object . get ( ) ;
2017-04-20 18:43:11 +00:00
}
uint32_t GetSerial ( uint32_t id ) {
2017-11-23 21:04:26 +00:00
if ( id > = mObjects . size ( ) ) {
2017-04-20 18:43:11 +00:00
return 0 ;
}
2017-11-23 21:04:26 +00:00
return mObjects [ id ] . serial ;
2017-04-20 18:38:20 +00:00
}
private :
uint32_t GetNewId ( ) {
2017-11-23 21:04:26 +00:00
if ( mFreeIds . empty ( ) ) {
return mCurrentId + + ;
2017-04-20 18:38:20 +00:00
}
2017-11-23 21:04:26 +00:00
uint32_t id = mFreeIds . back ( ) ;
mFreeIds . pop_back ( ) ;
2017-04-20 18:38:20 +00:00
return id ;
}
void FreeId ( uint32_t id ) {
2017-11-23 21:04:26 +00:00
mFreeIds . push_back ( id ) ;
2017-07-22 00:00:22 +00:00
}
2017-04-20 18:38:20 +00:00
// 0 is an ID reserved to represent nullptr
2017-11-23 21:04:26 +00:00
uint32_t mCurrentId = 1 ;
std : : vector < uint32_t > mFreeIds ;
std : : vector < ObjectAndSerial > mObjects ;
Device * mDevice ;
2017-04-20 18:38:20 +00:00
} ;
//* The client wire uses the global NXT device to store its global data such as the serializer
//* and the object id allocators.
class Device : public ObjectBase {
public :
Device ( CommandSerializer * serializer )
: ObjectBase ( this , 1 , 1 ) ,
{ % for type in by_category [ " object " ] if not type . name . canonical_case ( ) = = " device " % }
{ { type . name . camelCase ( ) } } ( this ) ,
{ % endfor % }
2017-11-23 21:04:26 +00:00
mSerializer ( serializer ) {
2017-04-20 18:38:20 +00:00
}
void * GetCmdSpace ( size_t size ) {
2017-11-23 21:04:26 +00:00
return mSerializer - > GetCmdSpace ( size ) ;
2017-04-20 18:38:20 +00:00
}
{ % for type in by_category [ " object " ] if not type . name . canonical_case ( ) = = " device " % }
ObjectAllocator < { { type . name . CamelCase ( ) } } > { { type . name . camelCase ( ) } } ;
{ % endfor % }
2017-06-09 14:51:55 +00:00
void HandleError ( const char * message ) {
if ( errorCallback ) {
errorCallback ( message , errorUserdata ) ;
}
}
2017-04-20 18:42:36 +00:00
nxtDeviceErrorCallback errorCallback = nullptr ;
nxtCallbackUserdata errorUserdata ;
2017-04-20 18:38:20 +00:00
private :
2017-11-23 21:04:26 +00:00
CommandSerializer * mSerializer = nullptr ;
2017-04-20 18:38:20 +00:00
} ;
//* Implementation of the client API functions.
{ % for type in by_category [ " object " ] % }
{ % set Type = type . name . CamelCase ( ) % }
{ % for method in type . methods % }
{ % set Suffix = as_MethodSuffix ( type . name , method . name ) % }
{ { as_backendType ( method . return_type ) } } Client { { Suffix } } (
{ { - as_backendType ( type ) } } self
{ % - for arg in method . arguments - % }
, { { as_annotated_backendType ( arg ) } }
{ % - endfor - % }
) {
Device * device = self - > device ;
wire : : { { Suffix } } Cmd cmd ;
//* Create the structure going on the wire on the stack and fill it with the value
//* arguments so it can compute its size.
{
//* Value objects are stored as IDs
{ % for arg in method . arguments if arg . annotation = = " value " % }
{ % if arg . type . category = = " object " % }
cmd . { { as_varName ( arg . name ) } } = { { as_varName ( arg . name ) } } - > id ;
{ % else % }
cmd . { { as_varName ( arg . name ) } } = { { as_varName ( arg . name ) } } ;
{ % endif % }
{ % endfor % }
cmd . self = self - > id ;
//* The length of const char* is considered a value argument.
{ % for arg in method . arguments if arg . length = = " strlen " % }
cmd . { { as_varName ( arg . name ) } } Strlen = strlen ( { { as_varName ( arg . name ) } } ) ;
{ % endfor % }
}
//* Allocate space to send the command and copy the value args over.
size_t requiredSize = cmd . GetRequiredSize ( ) ;
auto allocCmd = reinterpret_cast < decltype ( cmd ) * > ( device - > GetCmdSpace ( requiredSize ) ) ;
* allocCmd = cmd ;
//* In the allocated space, write the non-value arguments.
{ % for arg in method . arguments if arg . annotation ! = " value " % }
{ % set argName = as_varName ( arg . name ) % }
{ % if arg . length = = " strlen " % }
memcpy ( allocCmd - > GetPtr_ { { argName } } ( ) , { { argName } } , allocCmd - > { { argName } } Strlen + 1 ) ;
{ % elif arg . type . category = = " object " % }
auto { { argName } } Storage = reinterpret_cast < uint32_t * > ( allocCmd - > GetPtr_ { { argName } } ( ) ) ;
for ( size_t i = 0 ; i < { { as_varName ( arg . length . name ) } } ; i + + ) {
{ { argName } } Storage [ i ] = { { argName } } [ i ] - > id ;
}
{ % else % }
memcpy ( allocCmd - > GetPtr_ { { argName } } ( ) , { { argName } } , { { as_varName ( arg . length . name ) } } * sizeof ( * { { argName } } ) ) ;
{ % endif % }
{ % endfor % }
//* For object creation, store the object ID the client will use for the result.
{ % if method . return_type . category = = " object " % }
2017-04-20 18:43:11 +00:00
auto * allocation = self - > device - > { { method . return_type . name . camelCase ( ) } } . New ( ) ;
{ % if type . is_builder % }
//* We are in GetResult, so the callback that should be called is the
//* currently set one. Copy it over to the created object and prevent the
//* builder from calling the callback on destruction.
allocation - > object - > builderCallback = self - > builderCallback ;
self - > builderCallback . canCall = false ;
{ % endif % }
allocCmd - > resultId = allocation - > object - > id ;
allocCmd - > resultSerial = allocation - > serial ;
return allocation - > object . get ( ) ;
2017-04-20 18:38:20 +00:00
{ % endif % }
}
{ % endfor % }
2017-05-08 13:17:44 +00:00
{ % if type . is_builder % }
2017-04-20 18:43:11 +00:00
void Client { { as_MethodSuffix ( type . name , Name ( " set error callback " ) ) } } ( { { Type } } * self ,
nxtBuilderErrorCallback callback ,
2017-05-08 13:17:44 +00:00
nxtCallbackUserdata userdata1 ,
nxtCallbackUserdata userdata2 ) {
2017-04-20 18:43:11 +00:00
self - > builderCallback . callback = callback ;
self - > builderCallback . userdata1 = userdata1 ;
self - > builderCallback . userdata2 = userdata2 ;
2017-05-08 13:17:44 +00:00
}
{ % endif % }
2017-04-20 18:38:20 +00:00
{ % if not type . name . canonical_case ( ) = = " device " % }
//* When an object's refcount reaches 0, notify the server side of it and delete it.
void Client { { as_MethodSuffix ( type . name , Name ( " release " ) ) } } ( { { Type } } * obj ) {
obj - > refcount - - ;
if ( obj - > refcount > 0 ) {
return ;
}
2017-04-20 18:43:11 +00:00
obj - > builderCallback . Call ( NXT_BUILDER_ERROR_STATUS_UNKNOWN , " Unknown " ) ;
2017-04-20 18:38:20 +00:00
wire : : { { as_MethodSuffix ( type . name , Name ( " destroy " ) ) } } Cmd cmd ;
cmd . objectId = obj - > id ;
size_t requiredSize = cmd . GetRequiredSize ( ) ;
auto allocCmd = reinterpret_cast < decltype ( cmd ) * > ( obj - > device - > GetCmdSpace ( requiredSize ) ) ;
* allocCmd = cmd ;
obj - > device - > { { type . name . camelCase ( ) } } . Free ( obj ) ;
}
void Client { { as_MethodSuffix ( type . name , Name ( " reference " ) ) } } ( { { Type } } * obj ) {
obj - > refcount + + ;
}
{ % endif % }
{ % endfor % }
2017-06-09 14:51:29 +00:00
void ClientBufferMapReadAsync ( Buffer * buffer , uint32_t start , uint32_t size , nxtBufferMapReadCallback callback , nxtCallbackUserdata userdata ) {
2017-06-09 14:51:55 +00:00
uint32_t serial = buffer - > readRequestSerial + + ;
2017-07-10 17:46:05 +00:00
ASSERT ( buffer - > readRequests . find ( serial ) = = buffer - > readRequests . end ( ) ) ;
2017-06-09 14:51:55 +00:00
Buffer : : MapReadRequestData request ;
request . callback = callback ;
request . userdata = userdata ;
request . size = size ;
buffer - > readRequests [ serial ] = request ;
wire : : BufferMapReadAsyncCmd cmd ;
cmd . bufferId = buffer - > id ;
cmd . requestSerial = serial ;
cmd . start = start ;
cmd . size = size ;
size_t requiredSize = cmd . GetRequiredSize ( ) ;
auto allocCmd = reinterpret_cast < decltype ( cmd ) * > ( buffer - > device - > GetCmdSpace ( requiredSize ) ) ;
* allocCmd = cmd ;
}
2018-03-21 00:56:39 +00:00
void ClientBufferMapWriteAsync ( Buffer * , uint32_t , uint32_t , nxtBufferMapWriteCallback , nxtCallbackUserdata ) {
// TODO(cwallez@chromium.org): Implement the wire for BufferMapWriteAsync
ASSERT ( false ) ;
}
2017-06-09 14:51:55 +00:00
void ProxyClientBufferUnmap ( Buffer * buffer ) {
//* Invalidate the local pointer, and cancel all other in-flight requests that would turn into
//* errors anyway (you can't double map). This prevents race when the following happens, where
//* the application code would have unmapped a buffer but still receive a callback:
//* - Client -> Server: MapRequest1, Unmap, MapRequest2
//* - Server -> Client: Result of MapRequest1
//* - Unmap locally on the client
//* - Server -> Client: Result of MapRequest2
if ( buffer - > mappedData ) {
free ( buffer - > mappedData ) ;
buffer - > mappedData = nullptr ;
}
2018-03-20 23:48:54 +00:00
buffer - > ClearMapRequests ( NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN ) ;
2017-06-09 14:51:55 +00:00
ClientBufferUnmap ( buffer ) ;
2017-06-09 14:51:29 +00:00
}
2017-07-07 18:39:38 +00:00
void ClientDeviceReference ( Device * ) {
2017-04-20 18:38:20 +00:00
}
2017-07-07 18:39:38 +00:00
void ClientDeviceRelease ( Device * ) {
2017-04-20 18:38:20 +00:00
}
2017-04-20 18:42:36 +00:00
void ClientDeviceSetErrorCallback ( Device * self , nxtDeviceErrorCallback callback , nxtCallbackUserdata userdata ) {
self - > errorCallback = callback ;
self - > errorUserdata = userdata ;
}
2017-06-09 14:51:55 +00:00
// Some commands don't have a custom wire format, but need to be handled manually to update
// some client-side state tracking. For these we have to functions:
// - An autogenerated Client{{suffix}} method that sends the command on the wire
// - A manual ProxyClient{{suffix}} method that will be inserted in the proctable instead of
// the autogenerated one, and that will have to call Client{{suffix}}
{ % set proxied_commands = [ " BufferUnmap " ] % }
2017-04-20 18:38:20 +00:00
nxtProcTable GetProcs ( ) {
nxtProcTable table ;
{ % for type in by_category [ " object " ] % }
{ % for method in native_methods ( type ) % }
2017-06-09 14:51:55 +00:00
{ % set suffix = as_MethodSuffix ( type . name , method . name ) % }
{ % if suffix in proxied_commands % }
table . { { as_varName ( type . name , method . name ) } } = reinterpret_cast < { { as_cProc ( type . name , method . name ) } } > ( ProxyClient { { suffix } } ) ;
{ % else % }
table . { { as_varName ( type . name , method . name ) } } = reinterpret_cast < { { as_cProc ( type . name , method . name ) } } > ( Client { { suffix } } ) ;
{ % endif % }
2017-04-20 18:38:20 +00:00
{ % endfor % }
{ % endfor % }
return table ;
}
2017-05-09 13:34:13 +00:00
class Client : public CommandHandler {
public :
2017-11-23 21:04:26 +00:00
Client ( Device * device ) : mDevice ( device ) {
2017-05-09 13:34:13 +00:00
}
const uint8_t * HandleCommands ( const uint8_t * commands , size_t size ) override {
2017-04-20 18:42:36 +00:00
while ( size > sizeof ( ReturnWireCmd ) ) {
ReturnWireCmd cmdId = * reinterpret_cast < const ReturnWireCmd * > ( commands ) ;
bool success = false ;
switch ( cmdId ) {
case ReturnWireCmd : : DeviceErrorCallback :
success = HandleDeviceErrorCallbackCmd ( & commands , & size ) ;
break ;
2017-04-20 18:43:11 +00:00
{ % for type in by_category [ " object " ] if type . is_builder % }
case ReturnWireCmd : : { { type . name . CamelCase ( ) } } ErrorCallback :
success = Handle { { type . name . CamelCase ( ) } } ErrorCallbackCmd ( & commands , & size ) ;
break ;
{ % endfor % }
2017-06-09 14:51:55 +00:00
case ReturnWireCmd : : BufferMapReadAsyncCallback :
success = HandleBufferMapReadAsyncCallback ( & commands , & size ) ;
break ;
2017-04-20 18:42:36 +00:00
default :
success = false ;
}
if ( ! success ) {
return nullptr ;
}
}
if ( size ! = 0 ) {
return nullptr ;
}
2017-05-09 13:34:13 +00:00
2017-04-20 18:42:36 +00:00
return commands ;
2017-05-09 13:34:13 +00:00
}
private :
2017-11-23 21:04:26 +00:00
Device * mDevice = nullptr ;
2017-04-20 18:42:36 +00:00
//* Helper function for the getting of the command data in command handlers.
//* Checks there is enough data left, updates the buffer / size and returns
//* the command (or nullptr for an error).
template < typename T >
static const T * GetCommand ( const uint8_t * * commands , size_t * size ) {
if ( * size < sizeof ( T ) ) {
return nullptr ;
}
const T * cmd = reinterpret_cast < const T * > ( * commands ) ;
size_t cmdSize = cmd - > GetRequiredSize ( ) ;
if ( * size < cmdSize ) {
return nullptr ;
}
* commands + = cmdSize ;
* size - = cmdSize ;
return cmd ;
}
bool HandleDeviceErrorCallbackCmd ( const uint8_t * * commands , size_t * size ) {
const auto * cmd = GetCommand < ReturnDeviceErrorCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
if ( cmd - > GetMessage ( ) [ cmd - > messageStrlen ] ! = ' \0 ' ) {
return false ;
}
2017-11-23 21:04:26 +00:00
mDevice - > HandleError ( cmd - > GetMessage ( ) ) ;
2017-04-20 18:42:36 +00:00
return true ;
}
2017-04-20 18:43:11 +00:00
{ % for type in by_category [ " object " ] if type . is_builder % }
{ % set Type = type . name . CamelCase ( ) % }
bool Handle { { Type } } ErrorCallbackCmd ( const uint8_t * * commands , size_t * size ) {
const auto * cmd = GetCommand < Return { { Type } } ErrorCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
if ( cmd - > GetMessage ( ) [ cmd - > messageStrlen ] ! = ' \0 ' ) {
return false ;
}
2017-11-23 21:04:26 +00:00
auto * builtObject = mDevice - > { { type . built_type . name . camelCase ( ) } } . GetObject ( cmd - > builtObjectId ) ;
uint32_t objectSerial = mDevice - > { { type . built_type . name . camelCase ( ) } } . GetSerial ( cmd - > builtObjectId ) ;
2017-04-20 18:43:11 +00:00
//* The object might have been deleted or a new object created with the same ID.
if ( builtObject = = nullptr | | objectSerial ! = cmd - > builtObjectSerial ) {
return true ;
}
2017-07-27 23:43:43 +00:00
bool called = builtObject - > builderCallback . Call ( static_cast < nxtBuilderErrorStatus > ( cmd - > status ) , cmd - > GetMessage ( ) ) ;
// Unhandled builder errors are forwarded to the device
if ( ! called & & cmd - > status ! = NXT_BUILDER_ERROR_STATUS_SUCCESS & & cmd - > status ! = NXT_BUILDER_ERROR_STATUS_UNKNOWN ) {
2017-11-23 21:04:26 +00:00
mDevice - > HandleError ( ( " Unhandled builder error: " + std : : string ( cmd - > GetMessage ( ) ) ) . c_str ( ) ) ;
2017-07-27 23:43:43 +00:00
}
2017-04-20 18:43:11 +00:00
return true ;
}
{ % endfor % }
2017-06-09 14:51:55 +00:00
bool HandleBufferMapReadAsyncCallback ( const uint8_t * * commands , size_t * size ) {
const auto * cmd = GetCommand < ReturnBufferMapReadAsyncCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
2017-11-23 21:04:26 +00:00
auto * buffer = mDevice - > buffer . GetObject ( cmd - > bufferId ) ;
uint32_t bufferSerial = mDevice - > buffer . GetSerial ( cmd - > bufferId ) ;
2017-06-09 14:51:55 +00:00
//* The buffer might have been deleted or recreated so this isn't an error.
if ( buffer = = nullptr | | bufferSerial ! = cmd - > bufferSerial ) {
return true ;
}
//* The requests can have been deleted via an Unmap so this isn't an error.
auto requestIt = buffer - > readRequests . find ( cmd - > requestSerial ) ;
if ( requestIt = = buffer - > readRequests . end ( ) ) {
return true ;
}
auto request = requestIt - > second ;
2018-03-20 23:11:41 +00:00
// Delete the request before calling the callback otherwise the callback could be fired a second time if for example buffer.Unmap() is called inside the callback.
buffer - > readRequests . erase ( requestIt ) ;
2017-06-09 14:51:55 +00:00
//* On success, we copy the data locally because the IPC buffer isn't valid outside of this function
2018-03-20 23:48:54 +00:00
if ( cmd - > status = = NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS ) {
2017-06-09 14:51:55 +00:00
//* The server didn't send the right amount of data, this is an error and could cause
//* the application to crash if we did call the callback.
if ( request . size ! = cmd - > dataLength ) {
return false ;
}
if ( buffer - > mappedData ! = nullptr ) {
return false ;
}
buffer - > mappedData = malloc ( request . size ) ;
memcpy ( buffer - > mappedData , cmd - > GetData ( ) , request . size ) ;
2017-06-13 14:45:55 +00:00
2018-03-20 23:48:54 +00:00
request . callback ( static_cast < nxtBufferMapAsyncStatus > ( cmd - > status ) , buffer - > mappedData , request . userdata ) ;
2017-06-13 14:45:55 +00:00
} else {
2018-03-20 23:48:54 +00:00
request . callback ( static_cast < nxtBufferMapAsyncStatus > ( cmd - > status ) , nullptr , request . userdata ) ;
2017-06-09 14:51:55 +00:00
}
return true ;
}
2017-05-09 13:34:13 +00:00
} ;
2017-04-20 18:38:20 +00:00
}
2017-05-09 13:34:13 +00:00
CommandHandler * NewClientDevice ( nxtProcTable * procs , nxtDevice * device , CommandSerializer * serializer ) {
auto clientDevice = new client : : Device ( serializer ) ;
* device = reinterpret_cast < nxtDeviceImpl * > ( clientDevice ) ;
2017-04-20 18:38:20 +00:00
* procs = client : : GetProcs ( ) ;
2017-05-09 13:34:13 +00:00
return new client : : Client ( clientDevice ) ;
2017-04-20 18:38:20 +00:00
}
}
}