Erlang 驱动程序 erl_errno 问题
Erlang driver erl_errno issue
我在玩erl_driver
。我的驱动程序的开始回调如下:
ErlDrvData drv_start(ErlDrvPort port, char* command) {
char* file_name = command + sizeof(drv_name);
GenTtyData* port_data = (GenTtyData*)driver_alloc(sizeof(GenTtyData));
erl_errno = gt_open(file_name, &port_data->file);
if (erl_errno != 0) {
// Assertion there is just to show you my intention
assert(erl_errno == ENOENT);
driver_free(port_data);
return ERL_DRV_ERROR_ERRNO;
}
return port_data;
}
ErlDrvData(开始)(ErlDrvPort 端口,char 命令)
This is called when the driver is instantiated, when open_port/2 is called. The driver should return a number >= 0 or a pointer, or if the driver can't be started, one of three error codes should be returned:
ERL_DRV_ERROR_GENERAL - general error, no error code
ERL_DRV_ERROR_ERRNO - error with error code in erl_errno
ERL_DRV_ERROR_BADARG - error, badarg
If an error code is returned, the port isn't started.
所以我希望 erlang:open_port/2
将发送包含错误描述的退出信号(例如 eacces
,或 enotty
)。但是,在测试中我有
{'EXIT',
{unknown,
[{erlang,open_port,[{spawn,"gen_tty_drv non_existent.tty"},[]],[]},
{gen_tty,open,1,
[{file,
"/home/vkovalev/workspace/gen_tty_drv/_build/test/lib/gen_tty/src/gen_tty.erl"},
{line,48}]},
{gen_tty_drv_SUITE,open_non_existent_file,1,
[{file,
"/home/vkovalev/workspace/gen_tty_drv/test/gen_tty_drv_SUITE.erl"},
{line,29}]},
{test_server,ts_tc,3,[{file,"test_server.erl"},{line,1450}]},
{test_server,run_test_case_eval1,6,
[{file,"test_server.erl"},{line,1026}]},
{test_server,run_test_case_eval,9,
[{file,"test_server.erl"},{line,968}]}]}}
erlang 似乎没有看到或无法解码错误原因并发送 unknown
。那么我的 start
回调有什么问题?
这似乎是文档或代码中的错误。如果调用驱动程序 start
函数的代码发现函数 returns ERL_DRV_ERROR_ERRNO
, it takes the error number from errno
, not erl_errno
(this is true as of otp master commit dc35ae6c, anyway , 大概是 Erlang 18.0-rc2 版本,以后的版本可能会有所不同).
为了证明这一点,我编写了一个显式设置 errno
的驱动程序 start
函数:
static ErlDrvData
my_driver_start(ErlDrvPort port, char *command)
{
errno = ETXTBSY;
return ERL_DRV_ERROR_ERRNO;
}
通过这种方法,我得到了你想要的结果:
1> my_driver:start().
** exception error: etxtbsy
in function open_port/2
called as open_port({spawn,my_driver},[binary])
in call from my_driver:start/0 (my_driver.erl, line 10)
但是,以同样的方式设置 erl_errno
并没有产生这个结果。如果我明确地将 errno
设置为垃圾值,然后将 erl_errno
设置为有效的错误编号,如下所示:
static ErlDrvData
my_driver_start(ErlDrvPort port, char *command)
{
errno = 3245690989;
erl_errno = ETXTBSY;
return ERL_DRV_ERROR_ERRNO;
}
然后我得到与你相同的 unknown
结果:
1> my_driver:start().
** exception error: unknown
in function open_port/2
called as open_port({spawn,my_driver},[binary])
in call from my_driver:start/0 (my_driver.erl, line 10)
更新:我就此联系了 OTP 团队的一名成员,他们通过电子邮件回复说这是文档错误,这意味着使用 errno
而不是 erl_errno
正确的解决方案。他们说这部分驱动代码一直用errno
.
更新 2:自 Erlang 18.0 起,此问题已在 driver_entry
documentation 中修复。
我在玩erl_driver
。我的驱动程序的开始回调如下:
ErlDrvData drv_start(ErlDrvPort port, char* command) {
char* file_name = command + sizeof(drv_name);
GenTtyData* port_data = (GenTtyData*)driver_alloc(sizeof(GenTtyData));
erl_errno = gt_open(file_name, &port_data->file);
if (erl_errno != 0) {
// Assertion there is just to show you my intention
assert(erl_errno == ENOENT);
driver_free(port_data);
return ERL_DRV_ERROR_ERRNO;
}
return port_data;
}
ErlDrvData(开始)(ErlDrvPort 端口,char 命令)
This is called when the driver is instantiated, when open_port/2 is called. The driver should return a number >= 0 or a pointer, or if the driver can't be started, one of three error codes should be returned:
ERL_DRV_ERROR_GENERAL - general error, no error code
ERL_DRV_ERROR_ERRNO - error with error code in erl_errno
ERL_DRV_ERROR_BADARG - error, badarg
If an error code is returned, the port isn't started.
所以我希望 erlang:open_port/2
将发送包含错误描述的退出信号(例如 eacces
,或 enotty
)。但是,在测试中我有
{'EXIT',
{unknown,
[{erlang,open_port,[{spawn,"gen_tty_drv non_existent.tty"},[]],[]},
{gen_tty,open,1,
[{file,
"/home/vkovalev/workspace/gen_tty_drv/_build/test/lib/gen_tty/src/gen_tty.erl"},
{line,48}]},
{gen_tty_drv_SUITE,open_non_existent_file,1,
[{file,
"/home/vkovalev/workspace/gen_tty_drv/test/gen_tty_drv_SUITE.erl"},
{line,29}]},
{test_server,ts_tc,3,[{file,"test_server.erl"},{line,1450}]},
{test_server,run_test_case_eval1,6,
[{file,"test_server.erl"},{line,1026}]},
{test_server,run_test_case_eval,9,
[{file,"test_server.erl"},{line,968}]}]}}
erlang 似乎没有看到或无法解码错误原因并发送 unknown
。那么我的 start
回调有什么问题?
这似乎是文档或代码中的错误。如果调用驱动程序 start
函数的代码发现函数 returns ERL_DRV_ERROR_ERRNO
, it takes the error number from errno
, not erl_errno
(this is true as of otp master commit dc35ae6c, anyway , 大概是 Erlang 18.0-rc2 版本,以后的版本可能会有所不同).
为了证明这一点,我编写了一个显式设置 errno
的驱动程序 start
函数:
static ErlDrvData
my_driver_start(ErlDrvPort port, char *command)
{
errno = ETXTBSY;
return ERL_DRV_ERROR_ERRNO;
}
通过这种方法,我得到了你想要的结果:
1> my_driver:start().
** exception error: etxtbsy
in function open_port/2
called as open_port({spawn,my_driver},[binary])
in call from my_driver:start/0 (my_driver.erl, line 10)
但是,以同样的方式设置 erl_errno
并没有产生这个结果。如果我明确地将 errno
设置为垃圾值,然后将 erl_errno
设置为有效的错误编号,如下所示:
static ErlDrvData
my_driver_start(ErlDrvPort port, char *command)
{
errno = 3245690989;
erl_errno = ETXTBSY;
return ERL_DRV_ERROR_ERRNO;
}
然后我得到与你相同的 unknown
结果:
1> my_driver:start().
** exception error: unknown
in function open_port/2
called as open_port({spawn,my_driver},[binary])
in call from my_driver:start/0 (my_driver.erl, line 10)
更新:我就此联系了 OTP 团队的一名成员,他们通过电子邮件回复说这是文档错误,这意味着使用 errno
而不是 erl_errno
正确的解决方案。他们说这部分驱动代码一直用errno
.
更新 2:自 Erlang 18.0 起,此问题已在 driver_entry
documentation 中修复。