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
2018-06-06 15:36:49 +00:00
namespace nxt { namespace wire {
2017-04-20 18:38:20 +00:00
//* 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 ) {
2018-06-07 16:27:56 +00:00
for ( auto & it : requests ) {
if ( it . second . isWrite ) {
it . second . writeCallback ( status , nullptr , it . second . userdata ) ;
} else {
it . second . readCallback ( status , nullptr , it . second . userdata ) ;
}
2017-06-09 14:51:55 +00:00
}
2018-06-07 16:27:56 +00:00
requests . clear ( ) ;
2017-06-09 14:51:55 +00:00
}
//* 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.
2018-06-07 16:27:56 +00:00
struct MapRequestData {
nxtBufferMapReadCallback readCallback = nullptr ;
nxtBufferMapWriteCallback writeCallback = nullptr ;
2017-06-09 14:51:55 +00:00
nxtCallbackUserdata userdata = 0 ;
uint32_t size = 0 ;
2018-06-07 16:27:56 +00:00
bool isWrite = false ;
2017-06-09 14:51:55 +00:00
} ;
2018-06-07 16:27:56 +00:00
std : : map < uint32_t , MapRequestData > requests ;
uint32_t requestSerial = 0 ;
2017-06-09 14:51:55 +00:00
//* Only one mapped pointer can be active at a time because Unmap clears all the in-flight requests.
void * mappedData = nullptr ;
2018-06-07 16:27:56 +00:00
size_t mappedDataSize = 0 ;
bool isWriteMapped = false ;
2017-06-09 14:51:55 +00:00
} ;
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.
2018-06-06 15:36:49 +00:00
class Device : public ObjectBase , public wire : : ObjectIdProvider {
2017-04-20 18:38:20 +00:00
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 % }
2018-06-06 15:36:49 +00:00
// Implementation of the ObjectIdProvider interface
{ % for type in by_category [ " object " ] % }
ObjectId GetId ( { { as_cType ( type . name ) } } object ) const override {
return reinterpret_cast < { { as_backendType ( type ) } } > ( object ) - > id ;
}
{ % 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 } } (
2018-06-06 15:36:49 +00:00
{ { - as_cType ( type . name ) } } cSelf
2017-04-20 18:38:20 +00:00
{ % - for arg in method . arguments - % }
2018-06-06 15:36:49 +00:00
, { { as_annotated_cType ( arg ) } }
2017-04-20 18:38:20 +00:00
{ % - endfor - % }
) {
2018-06-06 15:36:49 +00:00
{ { as_backendType ( type ) } } self = reinterpret_cast < { { as_backendType ( type ) } } > ( cSelf ) ;
2017-04-20 18:38:20 +00:00
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.
2018-06-06 15:36:49 +00:00
cmd . self = cSelf ;
2017-04-20 18:38:20 +00:00
//* 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 % }
2018-06-06 15:36:49 +00:00
cmd . resultId = allocation - > object - > id ;
cmd . resultSerial = allocation - > serial ;
{ % endif % }
{ % for arg in method . arguments % }
cmd . { { as_varName ( arg . name ) } } = { { as_varName ( arg . name ) } } ;
{ % endfor % }
//* Allocate space to send the command and copy the value args over.
size_t requiredSize = cmd . GetRequiredSize ( ) ;
char * allocatedBuffer = static_cast < char * > ( device - > GetCmdSpace ( requiredSize ) ) ;
cmd . Serialize ( allocatedBuffer , * device ) ;
{ % if method . return_type . category = = " object " % }
2017-04-20 18:43:11 +00:00
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 ;
2018-06-06 15:36:49 +00:00
auto allocCmd = static_cast < decltype ( cmd ) * > ( obj - > device - > GetCmdSpace ( sizeof ( cmd ) ) ) ;
2017-04-20 18:38:20 +00:00
* 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 ) {
2018-06-07 16:27:56 +00:00
uint32_t serial = buffer - > requestSerial + + ;
ASSERT ( buffer - > requests . find ( serial ) = = buffer - > requests . end ( ) ) ;
2017-06-09 14:51:55 +00:00
2018-06-07 16:27:56 +00:00
Buffer : : MapRequestData request ;
request . readCallback = callback ;
2017-06-09 14:51:55 +00:00
request . userdata = userdata ;
request . size = size ;
2018-06-07 16:27:56 +00:00
request . isWrite = false ;
buffer - > requests [ serial ] = request ;
2017-06-09 14:51:55 +00:00
2018-06-07 16:27:56 +00:00
wire : : BufferMapAsyncCmd cmd ;
2017-06-09 14:51:55 +00:00
cmd . bufferId = buffer - > id ;
cmd . requestSerial = serial ;
cmd . start = start ;
cmd . size = size ;
2018-06-07 16:27:56 +00:00
cmd . isWrite = false ;
2017-06-09 14:51:55 +00:00
2018-06-06 15:36:49 +00:00
auto allocCmd = static_cast < decltype ( cmd ) * > ( buffer - > device - > GetCmdSpace ( sizeof ( cmd ) ) ) ;
2017-06-09 14:51:55 +00:00
* allocCmd = cmd ;
}
2018-06-07 16:27:56 +00:00
void ClientBufferMapWriteAsync ( Buffer * buffer , uint32_t start , uint32_t size , nxtBufferMapWriteCallback callback , nxtCallbackUserdata userdata ) {
uint32_t serial = buffer - > requestSerial + + ;
ASSERT ( buffer - > requests . find ( serial ) = = buffer - > requests . end ( ) ) ;
Buffer : : MapRequestData request ;
request . writeCallback = callback ;
request . userdata = userdata ;
request . size = size ;
request . isWrite = true ;
buffer - > requests [ serial ] = request ;
wire : : BufferMapAsyncCmd cmd ;
cmd . bufferId = buffer - > id ;
cmd . requestSerial = serial ;
cmd . start = start ;
cmd . size = size ;
cmd . isWrite = true ;
auto allocCmd = static_cast < decltype ( cmd ) * > ( buffer - > device - > GetCmdSpace ( sizeof ( cmd ) ) ) ;
* allocCmd = cmd ;
2018-03-21 00:56:39 +00:00
}
2018-06-06 15:36:49 +00:00
void ProxyClientBufferUnmap ( nxtBuffer cBuffer ) {
Buffer * buffer = reinterpret_cast < Buffer * > ( cBuffer ) ;
2017-06-09 14:51:55 +00:00
//* 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 ) {
2018-06-07 16:27:56 +00:00
// If the buffer was mapped for writing, send the update to the data to the server
if ( buffer - > isWriteMapped ) {
wire : : BufferUpdateMappedDataCmd cmd ;
cmd . bufferId = buffer - > id ;
cmd . dataLength = static_cast < uint32_t > ( buffer - > mappedDataSize ) ;
auto allocCmd = static_cast < decltype ( cmd ) * > ( buffer - > device - > GetCmdSpace ( sizeof ( cmd ) ) ) ;
* allocCmd = cmd ;
void * dataAlloc = buffer - > device - > GetCmdSpace ( cmd . dataLength ) ;
memcpy ( dataAlloc , buffer - > mappedData , cmd . dataLength ) ;
}
2017-06-09 14:51:55 +00:00
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
2018-06-06 15:36:49 +00:00
ClientBufferUnmap ( cBuffer ) ;
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
2018-06-07 16:27:56 +00:00
// some client-side state tracking. For these we have two functions:
2017-06-09 14:51:55 +00:00
// - 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
}
2018-06-06 15:36:49 +00:00
const char * HandleCommands ( const char * commands , size_t size ) override {
2018-06-21 01:54:18 +00:00
while ( size > = sizeof ( ReturnWireCmd ) ) {
2017-04-20 18:42:36 +00:00
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 ;
2018-06-07 16:27:56 +00:00
case ReturnWireCmd : : BufferMapWriteAsyncCallback :
success = HandleBufferMapWriteAsyncCallback ( & 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).
2018-06-06 15:36:49 +00:00
template < typename T >
static const T * GetData ( const char * * buffer , size_t * size , size_t count ) {
// TODO(cwallez@chromium.org): Check for overflow
size_t totalSize = count * sizeof ( T ) ;
if ( * size < totalSize ) {
2017-04-20 18:42:36 +00:00
return nullptr ;
}
2018-06-06 15:36:49 +00:00
const T * data = reinterpret_cast < const T * > ( * buffer ) ;
2017-04-20 18:42:36 +00:00
2018-06-06 15:36:49 +00:00
* buffer + = totalSize ;
* size - = totalSize ;
2017-04-20 18:42:36 +00:00
2018-06-06 15:36:49 +00:00
return data ;
}
template < typename T >
static const T * GetCommand ( const char * * commands , size_t * size ) {
return GetData < T > ( commands , size , 1 ) ;
2017-04-20 18:42:36 +00:00
}
2018-06-06 15:36:49 +00:00
bool HandleDeviceErrorCallbackCmd ( const char * * commands , size_t * size ) {
2017-04-20 18:42:36 +00:00
const auto * cmd = GetCommand < ReturnDeviceErrorCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
2018-06-06 15:36:49 +00:00
const char * message = GetData < char > ( commands , size , cmd - > messageStrlen + 1 ) ;
if ( message = = nullptr | | message [ cmd - > messageStrlen ] ! = ' \0 ' ) {
2017-04-20 18:42:36 +00:00
return false ;
}
2018-06-06 15:36:49 +00:00
mDevice - > HandleError ( message ) ;
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 ( ) % }
2018-06-06 15:36:49 +00:00
bool Handle { { Type } } ErrorCallbackCmd ( const char * * commands , size_t * size ) {
2017-04-20 18:43:11 +00:00
const auto * cmd = GetCommand < Return { { Type } } ErrorCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
2018-06-06 15:36:49 +00:00
const char * message = GetData < char > ( commands , size , cmd - > messageStrlen + 1 ) ;
if ( message = = nullptr | | message [ cmd - > messageStrlen ] ! = ' \0 ' ) {
2017-04-20 18:43:11 +00:00
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 ;
}
2018-06-06 15:36:49 +00:00
bool called = builtObject - > builderCallback . Call ( static_cast < nxtBuilderErrorStatus > ( cmd - > status ) , message ) ;
2017-07-27 23:43:43 +00:00
// Unhandled builder errors are forwarded to the device
if ( ! called & & cmd - > status ! = NXT_BUILDER_ERROR_STATUS_SUCCESS & & cmd - > status ! = NXT_BUILDER_ERROR_STATUS_UNKNOWN ) {
2018-06-06 15:36:49 +00:00
mDevice - > HandleError ( ( " Unhandled builder error: " + std : : string ( message ) ) . 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
2018-06-06 15:36:49 +00:00
bool HandleBufferMapReadAsyncCallback ( const char * * commands , size_t * size ) {
2017-06-09 14:51:55 +00:00
const auto * cmd = GetCommand < ReturnBufferMapReadAsyncCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
2018-07-10 13:54:01 +00:00
//* Unconditionnally get the data from the buffer so that the correct amount of data is
//* consumed from the buffer, even when we ignore the command and early out.
const char * requestData = nullptr ;
if ( cmd - > status = = NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS ) {
requestData = GetData < char > ( commands , size , cmd - > dataLength ) ;
if ( requestData = = 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.
2018-06-07 16:27:56 +00:00
auto requestIt = buffer - > requests . find ( cmd - > requestSerial ) ;
if ( requestIt = = buffer - > requests . end ( ) ) {
2017-06-09 14:51:55 +00:00
return true ;
}
2018-06-07 16:27:56 +00:00
//* It is an error for the server to call the read callback when we asked for a map write
if ( requestIt - > second . isWrite ) {
return false ;
}
2017-06-09 14:51:55 +00:00
auto request = requestIt - > second ;
2018-07-10 13:54:01 +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.
2018-06-07 16:27:56 +00:00
buffer - > requests . 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 ;
}
2018-07-10 13:54:01 +00:00
ASSERT ( requestData ! = nullptr ) ;
2018-06-06 15:36:49 +00:00
2018-07-10 13:54:01 +00:00
if ( buffer - > mappedData ! = nullptr ) {
2018-06-06 15:36:49 +00:00
return false ;
}
2018-06-07 16:27:56 +00:00
buffer - > isWriteMapped = false ;
buffer - > mappedDataSize = request . size ;
2017-06-09 14:51:55 +00:00
buffer - > mappedData = malloc ( request . size ) ;
2018-06-06 15:36:49 +00:00
memcpy ( buffer - > mappedData , requestData , request . size ) ;
2017-06-13 14:45:55 +00:00
2018-06-07 16:27:56 +00:00
request . readCallback ( static_cast < nxtBufferMapAsyncStatus > ( cmd - > status ) , buffer - > mappedData , request . userdata ) ;
} else {
request . readCallback ( static_cast < nxtBufferMapAsyncStatus > ( cmd - > status ) , nullptr , request . userdata ) ;
}
return true ;
}
bool HandleBufferMapWriteAsyncCallback ( const char * * commands , size_t * size ) {
const auto * cmd = GetCommand < ReturnBufferMapWriteAsyncCallbackCmd > ( commands , size ) ;
if ( cmd = = nullptr ) {
return false ;
}
auto * buffer = mDevice - > buffer . GetObject ( cmd - > bufferId ) ;
uint32_t bufferSerial = mDevice - > buffer . GetSerial ( cmd - > bufferId ) ;
//* 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 - > requests . find ( cmd - > requestSerial ) ;
if ( requestIt = = buffer - > requests . end ( ) ) {
return true ;
}
//* It is an error for the server to call the write callback when we asked for a map read
if ( ! requestIt - > second . isWrite ) {
return false ;
}
auto request = requestIt - > second ;
//* 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 - > requests . erase ( requestIt ) ;
//* On success, we copy the data locally because the IPC buffer isn't valid outside of this function
if ( cmd - > status = = NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS ) {
if ( buffer - > mappedData ! = nullptr ) {
return false ;
}
buffer - > isWriteMapped = true ;
buffer - > mappedDataSize = request . size ;
buffer - > mappedData = malloc ( request . size ) ;
memset ( buffer - > mappedData , 0 , request . size ) ;
request . writeCallback ( static_cast < nxtBufferMapAsyncStatus > ( cmd - > status ) , buffer - > mappedData , request . userdata ) ;
2017-06-13 14:45:55 +00:00
} else {
2018-06-07 16:27:56 +00:00
request . writeCallback ( 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
}
2018-06-06 15:36:49 +00:00
} } // namespace nxt::wire