mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-08-23 14:08:11 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6657ded4fa | ||
|
|
29ea9412a6 | ||
|
|
70adb1b4ce | ||
|
|
3f545becce |
+22
-26
@@ -78,19 +78,21 @@ common_json_value::common_json_value(const common_json & val) :
|
||||
common_json_value::common_json_value(common_json && val) :
|
||||
type(VAL_JSON), val_json(std::make_shared<common_json>(std::move(val))) {}
|
||||
|
||||
// the ctors and get<T>() below are explicit specializations, giving strong symbols
|
||||
// an explicit instantiation is a weak symbol, dropped by some LTO builds (clang-cl)
|
||||
template <typename T>
|
||||
common_json_value::common_json_value(const std::set<T> & vals) : type(VAL_JSON) {
|
||||
static std::shared_ptr<common_json> set_json(const std::set<T> & vals) {
|
||||
common_json out = common_json::array();
|
||||
|
||||
for (const auto & val : vals) {
|
||||
out.push_back(val);
|
||||
}
|
||||
|
||||
val_json = std::make_shared<common_json>(std::move(out));
|
||||
return std::make_shared<common_json>(std::move(out));
|
||||
}
|
||||
|
||||
// a set value is usable only for the types below
|
||||
#define COMMON_JSON_SET(...) template common_json_value::common_json_value(const std::set<__VA_ARGS__> &);
|
||||
#define COMMON_JSON_SET(...) template <> common_json_value::common_json_value(const std::set<__VA_ARGS__> & vals) : type(VAL_JSON), val_json(set_json(vals)) {}
|
||||
|
||||
COMMON_JSON_SET(int)
|
||||
COMMON_JSON_SET(std::string)
|
||||
@@ -98,56 +100,45 @@ COMMON_JSON_SET(std::string)
|
||||
#undef COMMON_JSON_SET
|
||||
|
||||
template <typename T>
|
||||
common_json_value::common_json_value(const std::map<std::string, T> & vals) : type(VAL_JSON) {
|
||||
static std::shared_ptr<common_json> map_json(const T & vals) {
|
||||
common_json out = common_json::object();
|
||||
|
||||
for (const auto & val : vals) {
|
||||
out.set({ val.first, val.second });
|
||||
}
|
||||
|
||||
val_json = std::make_shared<common_json>(std::move(out));
|
||||
return std::make_shared<common_json>(std::move(out));
|
||||
}
|
||||
|
||||
// a map value is usable only for the types below
|
||||
#define COMMON_JSON_MAP(...) template common_json_value::common_json_value(const std::map<std::string, __VA_ARGS__> &);
|
||||
#define COMMON_JSON_MAP(...) template <> common_json_value::common_json_value(const std::map<std::string, __VA_ARGS__> & vals) : type(VAL_JSON), val_json(map_json(vals)) {}
|
||||
|
||||
COMMON_JSON_MAP(bool)
|
||||
COMMON_JSON_MAP(std::string)
|
||||
|
||||
#undef COMMON_JSON_MAP
|
||||
|
||||
template <typename T>
|
||||
common_json_value::common_json_value(const std::unordered_map<std::string, T> & vals) : type(VAL_JSON) {
|
||||
common_json out = common_json::object();
|
||||
|
||||
for (const auto & val : vals) {
|
||||
out.set({ val.first, val.second });
|
||||
}
|
||||
|
||||
val_json = std::make_shared<common_json>(std::move(out));
|
||||
}
|
||||
|
||||
// an unordered map value is usable only for the types below
|
||||
#define COMMON_JSON_UMAP(...) template common_json_value::common_json_value(const std::unordered_map<std::string, __VA_ARGS__> &);
|
||||
#define COMMON_JSON_UMAP(...) template <> common_json_value::common_json_value(const std::unordered_map<std::string, __VA_ARGS__> & vals) : type(VAL_JSON), val_json(map_json(vals)) {}
|
||||
|
||||
COMMON_JSON_UMAP(size_t)
|
||||
|
||||
#undef COMMON_JSON_UMAP
|
||||
|
||||
template <typename T>
|
||||
common_json_value::common_json_value(const std::vector<T> & vals) : type(VAL_JSON) {
|
||||
static std::shared_ptr<common_json> vec_json(const std::vector<T> & vals) {
|
||||
common_json out = common_json::array();
|
||||
|
||||
for (const auto & val : vals) {
|
||||
out.push_back(val);
|
||||
}
|
||||
|
||||
val_json = std::make_shared<common_json>(std::move(out));
|
||||
return std::make_shared<common_json>(std::move(out));
|
||||
}
|
||||
|
||||
// a vector value is usable only for the types below
|
||||
// note: std::vector<bool> is not here, its proxy reference does not convert
|
||||
#define COMMON_JSON_VEC(...) template common_json_value::common_json_value(const std::vector<__VA_ARGS__> &);
|
||||
#define COMMON_JSON_VEC(...) template <> common_json_value::common_json_value(const std::vector<__VA_ARGS__> & vals) : type(VAL_JSON), val_json(vec_json(vals)) {}
|
||||
|
||||
COMMON_JSON_VEC(int)
|
||||
COMMON_JSON_VEC(unsigned char)
|
||||
@@ -404,10 +395,6 @@ common_json::items_view common_json::items() const {
|
||||
return items_view(const_cast<common_json *>(this), size());
|
||||
}
|
||||
|
||||
template <typename T> T common_json::get() const {
|
||||
return guard([&] { return as_json(this).get<T>(); });
|
||||
}
|
||||
|
||||
// the backing library cannot build a common_json, so this one is just a copy
|
||||
template <> common_json common_json::get<common_json>() const {
|
||||
return *this;
|
||||
@@ -415,7 +402,7 @@ template <> common_json common_json::get<common_json>() const {
|
||||
|
||||
// get<T>() is usable only for the types below
|
||||
|
||||
#define COMMON_JSON_GET(...) template __VA_ARGS__ common_json::get<__VA_ARGS__>() const;
|
||||
#define COMMON_JSON_GET(...) template <> __VA_ARGS__ common_json::get<__VA_ARGS__>() const { return guard([&] { return as_json(this).get<__VA_ARGS__>(); }); }
|
||||
|
||||
COMMON_JSON_GET(bool)
|
||||
COMMON_JSON_GET(int)
|
||||
@@ -435,3 +422,12 @@ COMMON_JSON_GET(std::vector<size_t>)
|
||||
COMMON_JSON_GET(std::unordered_map<std::string, size_t>)
|
||||
|
||||
#undef COMMON_JSON_GET
|
||||
|
||||
// must stay below the get<std::string> specialization
|
||||
common_json::operator std::string() const {
|
||||
return get<std::string>();
|
||||
}
|
||||
|
||||
std::string common_json::value(const std::string & key, const char * def) const {
|
||||
return contains(key) ? at(key).get<std::string>() : std::string(def);
|
||||
}
|
||||
|
||||
+2
-4
@@ -221,16 +221,14 @@ class common_json {
|
||||
// implicit get<T>() for plain values, so they can be assigned to their C++ type directly
|
||||
// note: kept to this short list on purpose, a wider one makes j["key"] ambiguous
|
||||
// note: a numeric one would make "str = json;" ambiguous, a number converts to char too
|
||||
operator std::string() const { return get<std::string>(); }
|
||||
operator std::string() const;
|
||||
|
||||
template <typename T>
|
||||
T value(const std::string & key, T def) const {
|
||||
return contains(key) ? at(key).get<T>() : def;
|
||||
}
|
||||
|
||||
std::string value(const std::string & key, const char * def) const {
|
||||
return contains(key) ? at(key).get<std::string>() : std::string(def);
|
||||
}
|
||||
std::string value(const std::string & key, const char * def) const;
|
||||
|
||||
// a JSON default needs no get<T>(), it is already the right type
|
||||
common_json value(const std::string & key, const common_json & def) const {
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "ggml-cuda/out-prod.cuh"
|
||||
#include "ggml-cuda/pad.cuh"
|
||||
#include "ggml-cuda/pool2d.cuh"
|
||||
#include "ggml-cuda/pool1d.cuh"
|
||||
#include "ggml-cuda/quantize.cuh"
|
||||
#include "ggml-cuda/rope.cuh"
|
||||
#include "ggml-cuda/roll.cuh"
|
||||
@@ -2326,6 +2327,9 @@ static bool ggml_cuda_compute_forward(ggml_backend_cuda_context & ctx, struct gg
|
||||
case GGML_OP_POOL_2D:
|
||||
ggml_cuda_op_pool2d(ctx, dst);
|
||||
break;
|
||||
case GGML_OP_POOL_1D:
|
||||
ggml_cuda_op_pool1d(ctx, dst);
|
||||
break;
|
||||
case GGML_OP_SUM:
|
||||
ggml_cuda_op_sum(ctx, dst);
|
||||
break;
|
||||
@@ -5245,6 +5249,7 @@ static bool ggml_backend_cuda_device_supports_op(ggml_backend_dev_t dev, const g
|
||||
case GGML_OP_CONV_2D_DW:
|
||||
return op->src[0]->type == GGML_TYPE_F32;
|
||||
case GGML_OP_CONV_TRANSPOSE_2D:
|
||||
case GGML_OP_POOL_1D:
|
||||
case GGML_OP_POOL_2D:
|
||||
return true;
|
||||
case GGML_OP_ACC:
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "pool1d.cuh"
|
||||
|
||||
static __global__ void pool1d_nchw_kernel(
|
||||
const int iw, const int ow,
|
||||
const int kw, const int sw, const int pw,
|
||||
const int parallel_elements,
|
||||
const float * src, float * dst, const enum ggml_op_pool op) {
|
||||
const int idx = threadIdx.x + blockIdx.x * blockDim.x;
|
||||
if (idx >= parallel_elements) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int nc = idx / ow;
|
||||
const int cur_ow = idx % ow;
|
||||
|
||||
const float * i_ptr = src + nc * iw;
|
||||
float * o_ptr = dst + nc * ow;
|
||||
|
||||
const int start = cur_ow * sw - pw;
|
||||
const int b = max(0, start);
|
||||
const int e = min(iw, start + kw);
|
||||
|
||||
float res;
|
||||
switch (op) {
|
||||
case GGML_OP_POOL_AVG: res = 0.0f; break;
|
||||
case GGML_OP_POOL_MAX: res = -FLT_MAX; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (int i = b; i < e; i++) {
|
||||
#if __CUDA_ARCH__ >= 350
|
||||
float cur = __ldg(i_ptr + i);
|
||||
#else
|
||||
float cur = i_ptr[i];
|
||||
#endif
|
||||
switch (op) {
|
||||
case GGML_OP_POOL_AVG: res += cur; break;
|
||||
case GGML_OP_POOL_MAX: res = max(res, cur); break;
|
||||
default: break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
if (op == GGML_OP_POOL_AVG) {
|
||||
res = (count > 0) ? (res / count) : 0.0f;
|
||||
}
|
||||
|
||||
o_ptr[cur_ow] = res;
|
||||
}
|
||||
|
||||
static void pool1d_nchw_kernel_f32_f32_cuda(
|
||||
const int iw, const int ow,
|
||||
const int kw, const int sw, const int pw,
|
||||
const int parallel_elements,
|
||||
const float * src, float * dst, const enum ggml_op_pool op,
|
||||
cudaStream_t stream) {
|
||||
const int num_blocks = (parallel_elements + CUDA_POOL1D_BLOCK_SIZE - 1) / CUDA_POOL1D_BLOCK_SIZE;
|
||||
dim3 block_nums(num_blocks);
|
||||
pool1d_nchw_kernel<<<block_nums, CUDA_POOL1D_BLOCK_SIZE, 0, stream>>>(iw, ow, kw, sw, pw, parallel_elements, src, dst, op);
|
||||
}
|
||||
|
||||
void ggml_cuda_op_pool1d(ggml_backend_cuda_context & ctx, ggml_tensor * dst) {
|
||||
const ggml_tensor * src0 = dst->src[0];
|
||||
const float * src0_d = (const float *)src0->data;
|
||||
float * dst_d = (float *)dst->data;
|
||||
cudaStream_t stream = ctx.stream();
|
||||
|
||||
GGML_ASSERT(src0->type == GGML_TYPE_F32);
|
||||
GGML_ASSERT( dst->type == GGML_TYPE_F32);
|
||||
|
||||
const int32_t * opts = (const int32_t *)dst->op_params;
|
||||
enum ggml_op_pool op = static_cast<ggml_op_pool>(opts[0]);
|
||||
const int k0 = opts[1];
|
||||
const int s0 = opts[2];
|
||||
const int p0 = opts[3];
|
||||
|
||||
const int64_t IW = src0->ne[0];
|
||||
const int64_t OW = dst->ne[0];
|
||||
const int64_t nr = ggml_nrows(src0);
|
||||
|
||||
const int parallel_elements = (int)(nr * OW);
|
||||
|
||||
pool1d_nchw_kernel_f32_f32_cuda(IW, OW, k0, s0, p0, parallel_elements, src0_d, dst_d, op, stream);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "common.cuh"
|
||||
|
||||
#define CUDA_POOL1D_BLOCK_SIZE 256
|
||||
|
||||
void ggml_cuda_op_pool1d(ggml_backend_cuda_context & ctx, ggml_tensor * dst);
|
||||
@@ -955,6 +955,7 @@ struct vk_device_struct {
|
||||
vk_pipeline pipeline_diag[2];
|
||||
vk_pipeline pipeline_clamp[2];
|
||||
vk_pipeline pipeline_pad_f32;
|
||||
vk_pipeline pipeline_pad_reflect_1d_f32;
|
||||
vk_pipeline pipeline_roll_f32;
|
||||
vk_pipeline pipeline_repeat_i32, pipeline_repeat_back_f32;
|
||||
vk_pipeline pipeline_repeat_i16;
|
||||
@@ -5630,6 +5631,7 @@ static void ggml_vk_load_shaders(vk_device& device, vk_pipeline requested) {
|
||||
ggml_vk_create_pipeline(device, device->pipeline_diag[1], "diag_f16", diag_f16_len, diag_f16_data, "main", 2, sizeof(vk_op_unary_push_constants), {512, 1, 1}, {}, 1);
|
||||
|
||||
ggml_vk_create_pipeline(device, device->pipeline_pad_f32, "pad_f32", pad_f32_len, pad_f32_data, "main", 2, sizeof(vk_op_pad_push_constants), {512, 1, 1}, {}, 1);
|
||||
ggml_vk_create_pipeline(device, device->pipeline_pad_reflect_1d_f32, "pad_reflect_1d_f32", pad_reflect_1d_f32_len, pad_reflect_1d_f32_data, "main", 2, sizeof(vk_op_unary_push_constants), {512, 1, 1}, {}, 1);
|
||||
|
||||
ggml_vk_create_pipeline(device, device->pipeline_roll_f32, "roll_f32", roll_f32_len, roll_f32_data, "main", 2, sizeof(vk_op_unary_push_constants), {512, 1, 1}, {}, 1);
|
||||
|
||||
@@ -11336,6 +11338,11 @@ static vk_pipeline ggml_vk_op_get_pipeline(ggml_backend_vk_context * ctx, const
|
||||
return ctx->device->pipeline_pad_f32;
|
||||
}
|
||||
return nullptr;
|
||||
case GGML_OP_PAD_REFLECT_1D:
|
||||
if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
||||
return ctx->device->pipeline_pad_reflect_1d_f32;
|
||||
}
|
||||
return nullptr;
|
||||
case GGML_OP_ROLL:
|
||||
if (src0->type == GGML_TYPE_F32 && dst->type == GGML_TYPE_F32) {
|
||||
return ctx->device->pipeline_roll_f32;
|
||||
@@ -12239,6 +12246,7 @@ static void ggml_vk_op_f32(ggml_backend_vk_context * ctx, vk_context& subctx, co
|
||||
case GGML_OP_CLAMP:
|
||||
case GGML_OP_LEAKY_RELU:
|
||||
case GGML_OP_PAD:
|
||||
case GGML_OP_PAD_REFLECT_1D:
|
||||
case GGML_OP_ROLL:
|
||||
case GGML_OP_REPEAT:
|
||||
case GGML_OP_REPEAT_BACK:
|
||||
@@ -13111,6 +13119,17 @@ static void ggml_vk_pad(ggml_backend_vk_context * ctx, vk_context& subctx, const
|
||||
ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_PAD, std::move(p));
|
||||
}
|
||||
|
||||
static void ggml_vk_pad_reflect_1d(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst) {
|
||||
const uint32_t p0 = (uint32_t)dst->op_params[0];
|
||||
const uint32_t p1 = (uint32_t)dst->op_params[1];
|
||||
|
||||
vk_op_unary_push_constants p = vk_op_unary_push_constants_init(src0, dst, ggml_nelements(dst));
|
||||
memcpy(&p.param1, &p0, sizeof(float));
|
||||
memcpy(&p.param2, &p1, sizeof(float));
|
||||
|
||||
ggml_vk_op_f32(ctx, subctx, src0, nullptr, nullptr, nullptr, dst, GGML_OP_PAD_REFLECT_1D, std::move(p));
|
||||
}
|
||||
|
||||
static void ggml_vk_roll(ggml_backend_vk_context * ctx, vk_context& subctx, const ggml_tensor * src0, ggml_tensor * dst) {
|
||||
const int32_t s0 = ggml_get_op_params_i32(dst, 0);
|
||||
const int32_t s1 = ggml_get_op_params_i32(dst, 1);
|
||||
@@ -15520,6 +15539,10 @@ static bool ggml_vk_build_graph(ggml_backend_vk_context * ctx, ggml_cgraph * cgr
|
||||
case GGML_OP_PAD:
|
||||
ggml_vk_pad(ctx, compute_ctx, src0, node);
|
||||
|
||||
break;
|
||||
case GGML_OP_PAD_REFLECT_1D:
|
||||
ggml_vk_pad_reflect_1d(ctx, compute_ctx, src0, node);
|
||||
|
||||
break;
|
||||
case GGML_OP_ROLL:
|
||||
ggml_vk_roll(ctx, compute_ctx, src0, node);
|
||||
@@ -18446,6 +18469,7 @@ static bool ggml_backend_vk_device_supports_op(ggml_backend_dev_t dev, const ggm
|
||||
case GGML_OP_SCALE:
|
||||
return ggml_is_contiguous(op->src[0]) && op->src[0]->type == GGML_TYPE_F32;
|
||||
case GGML_OP_PAD:
|
||||
case GGML_OP_PAD_REFLECT_1D:
|
||||
case GGML_OP_ROLL:
|
||||
return op->src[0]->type == GGML_TYPE_F32;
|
||||
case GGML_OP_DIAG_MASK_INF:
|
||||
@@ -19228,6 +19252,8 @@ static void ggml_vk_check_results_0(ggml_backend_vk_context * ctx, ggml_cgraph *
|
||||
} else if (tensor->op == GGML_OP_PAD) {
|
||||
tensor_clone = ggml_pad_ext(ggml_ctx, src_clone[0], tensor->op_params[0], tensor->op_params[1], tensor->op_params[2], tensor->op_params[3],
|
||||
tensor->op_params[4], tensor->op_params[5], tensor->op_params[6], tensor->op_params[7]);
|
||||
} else if (tensor->op == GGML_OP_PAD_REFLECT_1D) {
|
||||
tensor_clone = ggml_pad_reflect_1d(ggml_ctx, src_clone[0], tensor->op_params[0], tensor->op_params[1]);
|
||||
} else if (tensor->op == GGML_OP_REPEAT) {
|
||||
tensor_clone = ggml_repeat(ggml_ctx, src_clone[0], tensor);
|
||||
} else if (tensor->op == GGML_OP_REPEAT_BACK) {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#version 450
|
||||
|
||||
#include "types.glsl"
|
||||
#include "generic_unary_head.glsl" // included to use functions like fastdiv etc.
|
||||
|
||||
layout(local_size_x = 512, local_size_y = 1, local_size_z = 1) in;
|
||||
|
||||
void main() {
|
||||
|
||||
const uint idx = get_idx();
|
||||
|
||||
if (idx >= p.ne) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint p0 = floatBitsToUint(p.param1);
|
||||
const uint p1 = floatBitsToUint(p.param2);
|
||||
|
||||
const uint i3 = fastdiv(idx, p.ne1_012mp, fastdiv_L(p.ne1_Ls, 0));
|
||||
const uint i3_offset = i3 * p.ne12 * p.ne11 * p.ne10;
|
||||
|
||||
const uint i2 = fastdiv(idx - i3_offset, p.ne1_01mp, fastdiv_L(p.ne1_Ls, 1));
|
||||
const uint i2_offset = i2 * p.ne11 * p.ne10;
|
||||
|
||||
const uint i1 = fastdiv(idx - i3_offset - i2_offset, p.ne1_0mp, fastdiv_L(p.ne1_Ls, 2));
|
||||
const uint i0 = idx - i3_offset - i2_offset - i1 * p.ne10;
|
||||
|
||||
uint src_col;
|
||||
|
||||
if (i0 < p0) {
|
||||
src_col = p0 - i0; // left pad area
|
||||
} else if (i0 < p0 + p.ne00) {
|
||||
src_col = i0 - p0; // center area
|
||||
} else {
|
||||
src_col = 2u * p.ne00 - 2u - (i0 - p0); // right pad area
|
||||
}
|
||||
|
||||
const uint src_idx = i3 * p.nb03 + i2 * p.nb02 + i1 * p.nb01 + src_col * p.nb00;
|
||||
const uint d_idx = i3 * p.nb13 + i2 * p.nb12 + i1 * p.nb11 + i0 * p.nb10;
|
||||
|
||||
// copy the computed value to the destination tensor
|
||||
data_d[get_doffset() + d_idx] = D_TYPE(data_a[get_aoffset() + src_idx]);
|
||||
}
|
||||
@@ -896,6 +896,7 @@ void process_shaders() {
|
||||
string_to_spv("scale_f32", "scale.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}, {"FLOAT_TYPE", "float"}});
|
||||
|
||||
string_to_spv("pad_f32", "pad.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
|
||||
string_to_spv("pad_reflect_1d_f32", "pad_reflect_1d.comp", {{"A_TYPE", "float"}, {"D_TYPE", "float"}});
|
||||
|
||||
string_to_spv("concat_i8", "concat.comp", {{"A_TYPE", "uint8_t"}, {"B_TYPE", "uint8_t"}, {"D_TYPE", "uint8_t"}});
|
||||
string_to_spv("concat_i16", "concat.comp", {{"A_TYPE", "uint16_t"}, {"B_TYPE", "uint16_t"}, {"D_TYPE", "uint16_t"}});
|
||||
|
||||
@@ -27,7 +27,7 @@ vendor = {
|
||||
f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/split.py": "split.py",
|
||||
f"https://raw.githubusercontent.com/yhirose/cpp-httplib/{HTTPLIB_VERSION}/LICENSE": "vendor/cpp-httplib/LICENSE",
|
||||
|
||||
"https://raw.githubusercontent.com/sheredom/subprocess.h/9ce0d701b6fb10f8f8c4445edd31e7c60a1237e3/subprocess.h": "vendor/sheredom/subprocess.h",
|
||||
"https://raw.githubusercontent.com/sheredom/subprocess.h/0dccaa9aa176dd6d7ef8afeca3c18d6e80a32795/subprocess.h": "vendor/sheredom/subprocess.h",
|
||||
|
||||
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/xxhash.c": "vendor/hash/xxhash/xxhash.c",
|
||||
f"https://raw.githubusercontent.com/Cyan4973/xxHash/{XXHASH_COMMIT}/xxhash.h": "vendor/hash/xxhash/xxhash.h",
|
||||
|
||||
Vendored
+363
-12
@@ -275,13 +275,46 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__NetBSD__)
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
/* Which spelling of the chdir file action the platform provides, if any.
|
||||
POSIX 2024 standardised posix_spawn_file_actions_addchdir; implementations
|
||||
that shipped it earlier called it ..._np. macOS 26 and NetBSD 10 use the
|
||||
standard name, glibc 2.29+, macOS 10.15+ and FreeBSD 13.1+ use the _np name,
|
||||
and AIX, NetBSD 9 and older, and OpenBSD provide neither. */
|
||||
#if !defined(SUBPROCESS_ADDCHDIR_IS_POSIX)
|
||||
#if (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000) || \
|
||||
(defined(__NetBSD__) && __NetBSD_Version__ >= 1000000000)
|
||||
#define SUBPROCESS_ADDCHDIR_IS_POSIX 1
|
||||
#else
|
||||
#define SUBPROCESS_ADDCHDIR_IS_POSIX 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Whether to launch the child with fork()+exec() instead of posix_spawn(),
|
||||
for platforms with no posix_spawn_file_actions_addchdir under either
|
||||
spelling: the child chdir()s before exec, and a close-on-exec pipe carries
|
||||
exec's errno back. Define this yourself to force either implementation. */
|
||||
#if !defined(SUBPROCESS_SPAWN_VIA_FORK)
|
||||
#if defined(_AIX) || defined(__OpenBSD__) || \
|
||||
(defined(__NetBSD__) && (__NetBSD_Version__ < 1000000000))
|
||||
#define SUBPROCESS_SPAWN_VIA_FORK 1
|
||||
#else
|
||||
#define SUBPROCESS_SPAWN_VIA_FORK 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Whether subprocess_create_ex can honour process_cwd. glibc only gained
|
||||
posix_spawn_file_actions_addchdir_np in 2.29, and macOS in 10.15; the SDKs
|
||||
mark it unavailable on iOS, tvOS and watchOS, where the undefined version
|
||||
macro folds to 0 and so answers correctly. Define this yourself to override
|
||||
the detection, for instance on musl older than 1.1.24. */
|
||||
#if !defined(SUBPROCESS_HAVE_CWD)
|
||||
#if defined(__GLIBC__)
|
||||
#if SUBPROCESS_SPAWN_VIA_FORK
|
||||
#define SUBPROCESS_HAVE_CWD 1
|
||||
#elif defined(__GLIBC__)
|
||||
#if __GLIBC_PREREQ(2, 29)
|
||||
#define SUBPROCESS_HAVE_CWD 1
|
||||
#else
|
||||
@@ -294,10 +327,13 @@ subprocess_weak int subprocess_alive(struct subprocess_s *const process);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Whether posix_spawn reports a failed exec back to the caller. glibc only
|
||||
started doing so in 2.24; before that the child silently exits with 127. */
|
||||
/* Whether a failed exec is reported back to the caller. The fork() path always
|
||||
reports it through its error pipe. glibc's posix_spawn only started doing so
|
||||
in 2.24; before that the child silently exits with 127. */
|
||||
#if !defined(SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS)
|
||||
#if defined(__GLIBC__)
|
||||
#if SUBPROCESS_SPAWN_VIA_FORK
|
||||
#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
|
||||
#elif defined(__GLIBC__)
|
||||
#if __GLIBC_PREREQ(2, 24)
|
||||
#define SUBPROCESS_SPAWN_REPORTS_EXEC_ERRORS 1
|
||||
#else
|
||||
@@ -342,6 +378,14 @@ typedef intptr_t subprocess_intptr_t;
|
||||
typedef size_t subprocess_size_t;
|
||||
#endif
|
||||
|
||||
/* SIZE_T is ULONG_PTR, which is not size_t: on Win32 both are 32 bits wide but
|
||||
unsigned long and unsigned int are still distinct types. */
|
||||
#ifdef _WIN64
|
||||
typedef subprocess_size_t subprocess_ulongptr_t;
|
||||
#else
|
||||
typedef unsigned long subprocess_ulongptr_t;
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wreserved-identifier"
|
||||
@@ -351,6 +395,7 @@ typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION;
|
||||
typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES;
|
||||
typedef struct _STARTUPINFOW *LPSTARTUPINFOW;
|
||||
typedef struct _OVERLAPPED *LPOVERLAPPED;
|
||||
typedef struct _PROC_THREAD_ATTRIBUTE_LIST *LPPROC_THREAD_ATTRIBUTE_LIST;
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
@@ -402,6 +447,11 @@ struct subprocess_startup_info_s {
|
||||
void *hStdError;
|
||||
};
|
||||
|
||||
struct subprocess_startup_info_ex_s {
|
||||
struct subprocess_startup_info_s startupInfo;
|
||||
void *attributeList;
|
||||
};
|
||||
|
||||
struct subprocess_overlapped_s {
|
||||
uintptr_t Internal;
|
||||
uintptr_t InternalHigh;
|
||||
@@ -451,6 +501,14 @@ __declspec(dllimport) int __stdcall CreateProcessW(
|
||||
const subprocess_wchar_t *, subprocess_wchar_t *, LPSECURITY_ATTRIBUTES,
|
||||
LPSECURITY_ATTRIBUTES, int, unsigned long, void *,
|
||||
const subprocess_wchar_t *, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
|
||||
__declspec(dllimport) int __stdcall
|
||||
InitializeProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST, unsigned long,
|
||||
unsigned long, subprocess_ulongptr_t *);
|
||||
__declspec(dllimport) int __stdcall UpdateProcThreadAttribute(
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST, unsigned long, subprocess_ulongptr_t, void *,
|
||||
subprocess_ulongptr_t, void *, subprocess_ulongptr_t *);
|
||||
__declspec(dllimport) void __stdcall
|
||||
DeleteProcThreadAttributeList(LPPROC_THREAD_ATTRIBUTE_LIST);
|
||||
__declspec(dllimport) int __stdcall MultiByteToWideChar(
|
||||
unsigned int, unsigned long, const char *, int, subprocess_wchar_t *, int);
|
||||
__declspec(dllimport) int __stdcall CloseHandle(void *);
|
||||
@@ -667,12 +725,104 @@ int subprocess_create_named_pipe_helper(void **rd, void **wr) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
/* Move a pipe end off 0, 1 or 2. Duplicating a descriptor onto itself is a
|
||||
no-op, so a pipe end already sitting on a standard descriptor would keep its
|
||||
FD_CLOEXEC and be closed by exec, leaving the child without that stream. */
|
||||
static int subprocess_fds_above_std(int fds[2]) {
|
||||
int fd_flags;
|
||||
int index;
|
||||
int moved;
|
||||
int saved_errno;
|
||||
|
||||
for (index = 0; index < 2; index++) {
|
||||
if (fds[index] > STDERR_FILENO) {
|
||||
continue;
|
||||
}
|
||||
|
||||
moved = fcntl(fds[index], F_DUPFD, STDERR_FILENO + 1);
|
||||
if (-1 != moved) {
|
||||
fd_flags = fcntl(moved, F_GETFD, 0);
|
||||
if ((-1 == fd_flags) ||
|
||||
(-1 == fcntl(moved, F_SETFD, fd_flags | FD_CLOEXEC))) {
|
||||
saved_errno = errno;
|
||||
close(moved);
|
||||
errno = saved_errno;
|
||||
moved = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (-1 == moved) {
|
||||
saved_errno = errno;
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
fds[0] = -1;
|
||||
fds[1] = -1;
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fds[index]);
|
||||
fds[index] = moved;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create pipes with close-on-exec set so later subprocesses do not inherit
|
||||
descriptors belonging to subprocesses which are already running. */
|
||||
static int subprocess_pipe_cloexec(int fds[2]) {
|
||||
int fd_flags;
|
||||
int index;
|
||||
int saved_errno;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__) || defined(__DragonFly__) || \
|
||||
(defined(__sun) && defined(__SVR4))
|
||||
if (0 == pipe2(fds, O_CLOEXEC)) {
|
||||
return subprocess_fds_above_std(fds);
|
||||
}
|
||||
|
||||
/* Older kernels can lack pipe2 even when the C library declares it. */
|
||||
if (ENOSYS != errno) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (0 != pipe(fds)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (index = 0; index < 2; index++) {
|
||||
fd_flags = fcntl(fds[index], F_GETFD, 0);
|
||||
if ((-1 == fd_flags) ||
|
||||
(-1 == fcntl(fds[index], F_SETFD, fd_flags | FD_CLOEXEC))) {
|
||||
saved_errno = errno;
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
fds[0] = -1;
|
||||
fds[1] = -1;
|
||||
errno = saved_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return subprocess_fds_above_std(fds);
|
||||
}
|
||||
#endif
|
||||
|
||||
int subprocess_create(const char *const commandLine[], int options,
|
||||
struct subprocess_s *const out_process) {
|
||||
return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL,
|
||||
SUBPROCESS_NULL, out_process);
|
||||
}
|
||||
|
||||
#if SUBPROCESS_SPAWN_VIA_FORK
|
||||
/* Not every platform declares execvpe: AIX exports it from libc without ever
|
||||
naming it in a header, and glibc hides it behind _GNU_SOURCE. */
|
||||
extern int execvpe(const char *, char *const *, char *const *);
|
||||
#endif
|
||||
|
||||
int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
const char *const environment[],
|
||||
const char *const process_cwd,
|
||||
@@ -692,6 +842,7 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
subprocess_size_t bs_run;
|
||||
unsigned long flags = 0;
|
||||
unsigned long last_error = 0;
|
||||
int attribute_list_initialized = 0;
|
||||
int result = subprocess_error_unknown;
|
||||
const unsigned int codePageUtf8 = 65001;
|
||||
const unsigned long mbErrInvalidChars = 0x00000008;
|
||||
@@ -699,6 +850,8 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
const unsigned long handleFlagInherit = 0x00000001;
|
||||
const unsigned long createNoWindow = 0x08000000;
|
||||
const unsigned long createUnicodeEnvironment = 0x00000400;
|
||||
const unsigned long extendedStartupInfoPresent = 0x00080000;
|
||||
const subprocess_size_t procThreadAttributeHandleList = 0x00020002;
|
||||
struct subprocess_subprocess_information_s processInfo = {SUBPROCESS_NULL,
|
||||
SUBPROCESS_NULL, 0,
|
||||
0};
|
||||
@@ -706,6 +859,11 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
SUBPROCESS_NULL, 1};
|
||||
subprocess_wchar_t empty_environment[2] = {0, 0};
|
||||
subprocess_wchar_t *used_environment = SUBPROCESS_NULL;
|
||||
subprocess_ulongptr_t attribute_list_size = 0;
|
||||
subprocess_size_t inherited_handle_count = 0;
|
||||
LPPROC_THREAD_ATTRIBUTE_LIST attribute_list = SUBPROCESS_NULL;
|
||||
void *inherited_handles[3];
|
||||
struct subprocess_startup_info_ex_s startInfoEx;
|
||||
struct subprocess_startup_info_s startInfo = {0,
|
||||
SUBPROCESS_NULL,
|
||||
SUBPROCESS_NULL,
|
||||
@@ -1080,6 +1238,44 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
}
|
||||
}
|
||||
|
||||
/* Restrict inheritance to this subprocess's standard streams. Without a
|
||||
handle list, concurrent subprocess_create calls can inherit each other's
|
||||
temporarily-inheritable child pipe handles. */
|
||||
inherited_handles[inherited_handle_count++] = startInfo.hStdInput;
|
||||
inherited_handles[inherited_handle_count++] = startInfo.hStdOutput;
|
||||
if (startInfo.hStdError != startInfo.hStdOutput) {
|
||||
inherited_handles[inherited_handle_count++] = startInfo.hStdError;
|
||||
}
|
||||
|
||||
InitializeProcThreadAttributeList(SUBPROCESS_NULL, 1, 0,
|
||||
&attribute_list_size);
|
||||
if (0 == attribute_list_size) {
|
||||
result = subprocess_error_spawn;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
attribute_list = SUBPROCESS_PTR_CAST(LPPROC_THREAD_ATTRIBUTE_LIST,
|
||||
_alloca(attribute_list_size));
|
||||
if (!attribute_list || !InitializeProcThreadAttributeList(
|
||||
attribute_list, 1, 0, &attribute_list_size)) {
|
||||
result = subprocess_error_spawn;
|
||||
goto cleanup;
|
||||
}
|
||||
attribute_list_initialized = 1;
|
||||
|
||||
if (!UpdateProcThreadAttribute(
|
||||
attribute_list, 0, procThreadAttributeHandleList, inherited_handles,
|
||||
inherited_handle_count * sizeof(inherited_handles[0]),
|
||||
SUBPROCESS_NULL, SUBPROCESS_NULL)) {
|
||||
result = subprocess_error_spawn;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
startInfoEx.startupInfo = startInfo;
|
||||
startInfoEx.startupInfo.cb = sizeof(startInfoEx);
|
||||
startInfoEx.attributeList = attribute_list;
|
||||
flags |= extendedStartupInfoPresent;
|
||||
|
||||
if (!CreateProcessW(
|
||||
SUBPROCESS_NULL,
|
||||
commandLineCombinedWide, // command line
|
||||
@@ -1090,7 +1286,7 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
used_environment, // used environment
|
||||
process_cwd_wide, // use specified current directory
|
||||
SUBPROCESS_PTR_CAST(LPSTARTUPINFOW,
|
||||
&startInfo), // STARTUPINFO pointer
|
||||
&startInfoEx), // STARTUPINFOEX pointer
|
||||
SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) {
|
||||
result = subprocess_error_from_windows_error(GetLastError());
|
||||
if (subprocess_error_unknown == result) {
|
||||
@@ -1099,6 +1295,9 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
DeleteProcThreadAttributeList(attribute_list);
|
||||
attribute_list_initialized = 0;
|
||||
|
||||
out_process->hProcess = processInfo.hProcess;
|
||||
processInfo.hProcess = SUBPROCESS_NULL;
|
||||
|
||||
@@ -1128,6 +1327,10 @@ int subprocess_create_ex(const char *const commandLine[], int options,
|
||||
cleanup:
|
||||
last_error = GetLastError();
|
||||
|
||||
if (attribute_list_initialized) {
|
||||
DeleteProcThreadAttributeList(attribute_list);
|
||||
}
|
||||
|
||||
if (subprocess_error_unknown == result) {
|
||||
result = subprocess_error_from_windows_error(last_error);
|
||||
}
|
||||
@@ -1173,15 +1376,20 @@ cleanup:
|
||||
int stderrfd[2] = {-1, -1};
|
||||
int fd, fd_flags;
|
||||
int async_no_wait;
|
||||
int actions_created = 0;
|
||||
int result = subprocess_error_unknown;
|
||||
int saved_errno = 0;
|
||||
int posix_error;
|
||||
pid_t child = 0;
|
||||
extern char **environ;
|
||||
char *const empty_environment[1] = {SUBPROCESS_NULL};
|
||||
posix_spawn_file_actions_t actions;
|
||||
char *const *used_environment;
|
||||
#if SUBPROCESS_SPAWN_VIA_FORK
|
||||
/* Pipe used to relay the child's exec() errno back to the parent. */
|
||||
int exec_errfd[2] = {-1, -1};
|
||||
#else
|
||||
int actions_created = 0;
|
||||
int posix_error;
|
||||
posix_spawn_file_actions_t actions;
|
||||
#endif
|
||||
|
||||
async_no_wait = subprocess_option_enable_async_no_wait ==
|
||||
(options & subprocess_option_enable_async_no_wait);
|
||||
@@ -1202,13 +1410,13 @@ cleanup:
|
||||
|
||||
memset(out_process, 0, sizeof(*out_process));
|
||||
|
||||
if (0 != pipe(stdinfd)) {
|
||||
if (0 != subprocess_pipe_cloexec(stdinfd)) {
|
||||
saved_errno = errno;
|
||||
result = subprocess_error_pipe;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (0 != pipe(stdoutfd)) {
|
||||
if (0 != subprocess_pipe_cloexec(stdoutfd)) {
|
||||
saved_errno = errno;
|
||||
result = subprocess_error_pipe;
|
||||
goto cleanup;
|
||||
@@ -1216,7 +1424,7 @@ cleanup:
|
||||
|
||||
if (subprocess_option_combined_stdout_stderr !=
|
||||
(options & subprocess_option_combined_stdout_stderr)) {
|
||||
if (0 != pipe(stderrfd)) {
|
||||
if (0 != subprocess_pipe_cloexec(stderrfd)) {
|
||||
saved_errno = errno;
|
||||
result = subprocess_error_pipe;
|
||||
goto cleanup;
|
||||
@@ -1240,6 +1448,136 @@ cleanup:
|
||||
used_environment = empty_environment;
|
||||
}
|
||||
|
||||
#if SUBPROCESS_SPAWN_VIA_FORK
|
||||
/* fork()+exec() instead of posix_spawn, so the child can chdir() first.
|
||||
exec_errfd[1] is close-on-exec: a successful exec closes it and the parent
|
||||
reads EOF; a failed exec writes errno through it before _exit. */
|
||||
if (0 != pipe(exec_errfd)) {
|
||||
saved_errno = errno;
|
||||
result = subprocess_error_pipe;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (-1 == fcntl(exec_errfd[1], F_SETFD, FD_CLOEXEC)) {
|
||||
saved_errno = errno;
|
||||
result = subprocess_error_spawn;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
child = fork();
|
||||
|
||||
if (child < 0) {
|
||||
saved_errno = errno;
|
||||
result = subprocess_error_spawn;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (0 == child) {
|
||||
/* Child. Everything below must stay async-signal-safe: after fork() in a
|
||||
threaded process only such functions may be called before exec. */
|
||||
int child_errno;
|
||||
|
||||
close(exec_errfd[0]);
|
||||
|
||||
if ((-1 == dup2(stdinfd[0], STDIN_FILENO)) ||
|
||||
(-1 == dup2(stdoutfd[1], STDOUT_FILENO))) {
|
||||
goto child_failed;
|
||||
}
|
||||
|
||||
if (subprocess_option_combined_stdout_stderr ==
|
||||
(options & subprocess_option_combined_stdout_stderr)) {
|
||||
if (-1 == dup2(STDOUT_FILENO, STDERR_FILENO)) {
|
||||
goto child_failed;
|
||||
}
|
||||
} else {
|
||||
if (-1 == dup2(stderrfd[1], STDERR_FILENO)) {
|
||||
goto child_failed;
|
||||
}
|
||||
}
|
||||
|
||||
/* The originals are only closed once they have been duplicated, so that a
|
||||
pipe end that already sits on 0, 1 or 2 is not closed out from under us. */
|
||||
if (stdinfd[0] > STDERR_FILENO) {
|
||||
close(stdinfd[0]);
|
||||
}
|
||||
if (stdinfd[1] > STDERR_FILENO) {
|
||||
close(stdinfd[1]);
|
||||
}
|
||||
if (stdoutfd[0] > STDERR_FILENO) {
|
||||
close(stdoutfd[0]);
|
||||
}
|
||||
if (stdoutfd[1] > STDERR_FILENO) {
|
||||
close(stdoutfd[1]);
|
||||
}
|
||||
if (stderrfd[0] > STDERR_FILENO) {
|
||||
close(stderrfd[0]);
|
||||
}
|
||||
if (stderrfd[1] > STDERR_FILENO) {
|
||||
close(stderrfd[1]);
|
||||
}
|
||||
|
||||
if (process_cwd && (0 != chdir(process_cwd))) {
|
||||
goto child_failed;
|
||||
}
|
||||
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wcast-qual"
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast"
|
||||
#endif
|
||||
if (subprocess_option_search_user_path ==
|
||||
(options & subprocess_option_search_user_path)) {
|
||||
execvpe(commandLine[0],
|
||||
SUBPROCESS_CONST_CAST(char *const *, commandLine),
|
||||
SUBPROCESS_CONST_CAST(char *const *, used_environment));
|
||||
} else {
|
||||
execve(commandLine[0],
|
||||
SUBPROCESS_CONST_CAST(char *const *, commandLine),
|
||||
SUBPROCESS_CONST_CAST(char *const *, used_environment));
|
||||
}
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
child_failed:
|
||||
child_errno = errno;
|
||||
/* Nothing useful can be done if this write fails; the parent then sees EOF
|
||||
and reports success, exactly as posix_spawn would without exec reporting. */
|
||||
(void)!write(exec_errfd[1], &child_errno, sizeof(child_errno));
|
||||
/* 127 is what POSIX requires posix_spawn's child to exit with when exec
|
||||
fails, so both implementations look the same to a caller. */
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* Parent. */
|
||||
close(exec_errfd[1]);
|
||||
exec_errfd[1] = -1;
|
||||
|
||||
{
|
||||
int child_errno = 0;
|
||||
ssize_t bytes_read;
|
||||
|
||||
do {
|
||||
bytes_read = read(exec_errfd[0], &child_errno, sizeof(child_errno));
|
||||
} while ((-1 == bytes_read) && (EINTR == errno));
|
||||
|
||||
close(exec_errfd[0]);
|
||||
exec_errfd[0] = -1;
|
||||
|
||||
if (bytes_read == (ssize_t)sizeof(child_errno)) {
|
||||
/* exec failed in the child. Reap it and surface the reason. */
|
||||
while ((-1 == waitpid(child, SUBPROCESS_NULL, 0)) && (EINTR == errno)) {
|
||||
}
|
||||
child = 0;
|
||||
saved_errno = child_errno;
|
||||
result = subprocess_error_from_errno(child_errno);
|
||||
if (subprocess_error_unknown == result) {
|
||||
result = subprocess_error_spawn;
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
#else
|
||||
posix_error = posix_spawn_file_actions_init(&actions);
|
||||
if (0 != posix_error) {
|
||||
saved_errno = posix_error;
|
||||
@@ -1253,7 +1591,7 @@ cleanup:
|
||||
|
||||
// Set working directory
|
||||
if (process_cwd) {
|
||||
#if defined(__NetBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= 260000)
|
||||
#if SUBPROCESS_ADDCHDIR_IS_POSIX
|
||||
posix_error = posix_spawn_file_actions_addchdir(&actions, process_cwd);
|
||||
#elif !SUBPROCESS_HAVE_CWD
|
||||
posix_error = ENOSYS;
|
||||
@@ -1406,6 +1744,7 @@ cleanup:
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
#endif /* SUBPROCESS_SPAWN_VIA_FORK */
|
||||
|
||||
// Close the stdin read end
|
||||
close(stdinfd[0]);
|
||||
@@ -1480,9 +1819,21 @@ cleanup:
|
||||
result = subprocess_error_from_errno(saved_errno);
|
||||
}
|
||||
|
||||
#if SUBPROCESS_SPAWN_VIA_FORK
|
||||
if (-1 != exec_errfd[0]) {
|
||||
close(exec_errfd[0]);
|
||||
exec_errfd[0] = -1;
|
||||
}
|
||||
|
||||
if (-1 != exec_errfd[1]) {
|
||||
close(exec_errfd[1]);
|
||||
exec_errfd[1] = -1;
|
||||
}
|
||||
#else
|
||||
if (actions_created) {
|
||||
posix_spawn_file_actions_destroy(&actions);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (0 != result) {
|
||||
if (child) {
|
||||
|
||||
Reference in New Issue
Block a user