Coverage Report

Created: 2026-10-17 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/hobbes/lib/hobbes/ipc/procman.C
Line
Count
Source
1
2
#include <hobbes/ipc/procman.H>
3
#include <hobbes/lang/preds/class.H>
4
#include <hobbes/util/str.H>
5
#include <memory>
6
7
namespace hobbes {
8
9
0
bool ProcManager::isSpawnedPid(const std::string& cmd, long pid) const {
10
0
  auto ce = this->procs.find(cmd);
11
0
  return (ce != this->procs.end()) && (ce->second.pid == pid);
12
0
}
13
14
0
const proc& ProcManager::lp(long pid) const {
15
0
  for (const auto& pe : this->procs) {
16
0
    if (pe.second.pid == pid) {
17
0
      return pe.second;
18
0
    }
19
0
  }
20
0
  throw std::runtime_error("Not a spawned process: " + str::from(pid));
21
0
}
22
23
78.0k
bool ProcManager::refine(const TEnvPtr&, const HasField& hf, MonoTypeUnifier* u, Definitions*) {
24
78.0k
  auto rty   = hf.recordType;
25
78.0k
  auto fname = hf.fieldName;
26
78.0k
  auto hasty = hf.fieldType;
27
28
78.0k
  size_t uc = u->size();
29
78.0k
  if (const TString* fn = is<TString>(fname)) {
30
78.0k
    if (const TLong* pid = pidTy(rty)) {
31
0
      mgu(hasty, refinedType(lp(pid->value()), fn->value(), hasty), u);
32
0
    }
33
78.0k
  }
34
78.0k
  return uc != u->size();
35
78.0k
}
36
37
568
bool ProcManager::satisfied(const TEnvPtr& tenv, const HasField& hf, Definitions* ds) const {
38
568
  auto dir   = hf.direction;
39
568
  auto rty   = hf.recordType;
40
568
  auto fname = hf.fieldName;
41
568
  auto hasty = hf.fieldType;
42
43
568
  if (dir == HasField::Write) { return false; }
44
45
568
  if (const TString* fn = is<TString>(fname)) {
46
568
    if (const TLong* pid = pidTy(rty)) {
47
0
      if (const Func* fty = is<Func>(hasty)) {
48
        // this ensures that we _can_ get an invocation ID for the requested function/type
49
0
        invocationID(lp(pid->value()), fn->value(), hasty);
50
51
        // then we're satisfied IFF we can pack the argument and unpack the result
52
0
        return isClassSatisfied(tenv, "BlockCodec", list(fty->argument()), ds) &&
53
0
               isClassSatisfied(tenv, "BlockCodec", list(fty->result()), ds);
54
0
      }
55
0
    }
56
568
  }
57
58
568
  return false;
59
568
}
60
61
403k
bool ProcManager::satisfiable(const TEnvPtr& tenv, const HasField& hf, Definitions* ds) const {
62
403k
  auto dir   = hf.direction;
63
403k
  auto rty   = hf.recordType;
64
403k
  auto fname = hf.fieldName;
65
403k
  auto hasty = hf.fieldType;
66
67
403k
  if (dir == HasField::Write) { return false; }
68
69
105k
  if (is<TVar>(rty) != nullptr) {
70
78.0k
    return true;
71
78.0k
  } else if (pidTy(rty) == nullptr) {
72
27.6k
    return false;
73
27.6k
  } else if ((is<TVar>(fname) != nullptr) || !isMonoSingular(hasty)) {
74
0
    return true;
75
0
  } else {
76
0
    return satisfied(tenv, hf, ds);
77
0
  }
78
105k
}
79
80
struct ProcManUnqualify : public switchExprTyFn {
81
  const ProcManager*   pthis;
82
  const TEnvPtr&       tenv;
83
  const ConstraintPtr& constraint;
84
  Definitions*         defs;
85
86
0
  ProcManUnqualify(const ProcManager* pthis, const TEnvPtr& tenv, const ConstraintPtr& cst, Definitions* defs) : pthis(pthis), tenv(tenv), constraint(cst), defs(defs) {
87
0
  }
88
89
0
  ExprPtr wrapWithTy(const QualTypePtr& qty, Expr* e) const override {
90
0
    ExprPtr result(e);
91
0
    result->type(removeConstraint(this->constraint, qty));
92
0
    ExprPtr aresult(new Assump(result, result->type(), result->la()));
93
0
    aresult->type(result->type());
94
0
    return aresult;
95
0
  }
96
97
0
  ExprPtr annotateTypes(const ExprPtr& e) const {
98
0
    ExprPtr sexp = validateType(this->tenv, e, this->defs);
99
0
100
0
    if (sexp->type()->constraints().empty()) {
101
0
      return sexp;
102
0
    } else {
103
0
      return unqualifyTypes(this->tenv, sexp, this->defs);
104
0
    }
105
0
  }
106
107
0
  ExprPtr with(const Fn* v) const override {
108
0
    const Func* fty = is<Func>(v->type()->monoType());
109
0
    if (fty == nullptr) {
110
0
      throw std::runtime_error("Internal error, expected annotated function type");
111
0
    }
112
0
    return wrapWithTy(v->type(),
113
0
      new Fn(
114
0
        v->varNames(), 
115
0
        switchOf(v->body(), ProcManUnqualify(this->pthis, fnFrame(this->tenv, v->varNames(), fty->parameters()), this->constraint, this->defs)),
116
0
        v->la()
117
0
      )
118
0
    );
119
0
  }
120
121
0
  ExprPtr with(const Let* v) const override {
122
0
    return wrapWithTy(v->type(),
123
0
      new Let(
124
0
        v->var(),
125
0
        switchOf(v->varExpr(),  *this),
126
0
        switchOf(v->bodyExpr(), ProcManUnqualify(this->pthis, fnFrame(this->tenv, list(v->var()), list(v->varExpr()->type()->monoType())), this->constraint, this->defs)),
127
0
        v->la()
128
0
      )
129
0
    );
130
0
  }
131
132
0
  const proc* isProcRef(const Proj* f) const {
133
0
    if (hasConstraint(this->constraint, f->type())) {
134
0
      if (const TLong* pid = pidTy(f->record()->type()->monoType())) {
135
0
        return &(pthis->lp(pid->value()));
136
0
      }
137
0
    }
138
0
    return nullptr;
139
0
  }
140
141
0
  static ConstraintPtr blockCodecCst(const MonoTypePtr& ty) {
142
0
    return std::make_shared<Constraint>("BlockCodec", list(ty));
143
0
  }
144
145
0
  ExprPtr blockWrite(const proc* p, const ExprPtr& e) const {
146
0
    const auto& la = e->la();
147
0
    ExprPtr we = fncall(var("writeTo", qualtype(list(blockCodecCst(e->type()->monoType())), functy(list(primty("int"), e->type()->monoType()), primty("unit"))), la), list(constant(p->write_fd, la), e), la);
148
0
    return unqualifyClass(this->tenv, "BlockCodec", list(e->type()->monoType()), we, this->defs);
149
0
  }
150
151
0
  ExprPtr blockRead(const proc* p, const MonoTypePtr& ty) const {
152
0
    auto la = LexicalAnnotation::null();
153
0
    ExprPtr re = fncall(var("readFrom", qualtype(list(blockCodecCst(ty)), functy(list(primty("int")), ty)), la), list(constant(p->read_fd, la)), la);
154
0
    return unqualifyClass(this->tenv, "BlockCodec", list(re->type()->monoType()), re, this->defs);
155
0
  }
156
157
  // let
158
  //   _ = writeTo(FD, <code>);
159
  //   _ = writeTo(FD, <args>)
160
  // in
161
  //   readFrom(FD) :: rty
162
0
  ExprPtr makeInvocation(const proc* p, const ExprPtr& pe, const std::string& fname, const MonoTypePtr& ftyv, const Exprs& args) const {
163
0
    const Func* fty = is<Func>(ftyv);
164
0
    if (fty == nullptr) { throw std::runtime_error("Internal error, process RPC call expected function type, not: " + show(ftyv)); }
165
166
0
    MkRecord::FieldDefs argtupv;
167
0
    for (size_t i = 0; i < args.size(); ++i) {
168
0
      argtupv.push_back(MkRecord::FieldDef(".f" + str::from(i), args[i]));
169
0
    }
170
0
    const auto& la = pe->la();
171
172
    // avoid partial remote invocations by completely evaluating arguments before sending remote procedure calls
173
0
    std::string arglVN = freshName();
174
0
    ExprPtr argl = mkrecord(argtupv, la);
175
176
0
    return
177
0
      let(arglVN,      argl,
178
0
      let(freshName(), blockWrite(p, constant(invocationID(*p, fname, ftyv), la)),
179
0
      let(freshName(), blockWrite(p, var(arglVN, argl->type(), la)),
180
0
      blockRead(p, fty->result()), la), la), la);
181
0
  }
182
183
0
  ExprPtr with(const App* v) const override {
184
0
    if (const Proj* f = is<Proj>(stripAssumpHead(v->fn()))) {
185
0
      if (const proc* p = isProcRef(f)) {
186
0
        return makeInvocation(p, switchOf(f->record(), *this), f->field(), f->type()->monoType(), switchOf(v->args(), *this));
187
0
      }
188
0
    }
189
0
    return wrapWithTy(v->type(), new App(switchOf(v->fn(), *this), switchOf(v->args(), *this), v->la()));
190
0
  }
191
192
0
  ExprPtr with(const Proj* v) const override {
193
    // generate a function for this reference to an external function
194
0
    if (const proc* p = isProcRef(v)) {
195
0
      const Func* fty = is<Func>(v->type()->monoType());
196
197
0
      MonoTypes argtys = fty->parameters();
198
0
      str::seq  vns;
199
0
      Exprs     args;
200
201
0
      for (const auto &argty : argtys) {
202
0
        std::string vname = freshName();
203
204
0
        vns.push_back(vname);
205
0
        args.push_back(var(vname, qualtype(argty), v->la()));
206
0
      }
207
208
0
      return wrapWithTy(v->type(), new Fn(vns, makeInvocation(p, switchOf(v->record(), *this), v->field(), v->type()->monoType(), args), v->la()));
209
0
    } else {
210
0
      return wrapWithTy(v->type(), new Proj(switchOf(v->record(), *this), v->field(), v->la()));
211
0
    }
212
0
  }
213
};
214
215
0
ExprPtr ProcManager::unqualify(const TEnvPtr& tenv, const ConstraintPtr& cst, const ExprPtr& e, Definitions* ds) const {
216
0
  return switchOf(e, ProcManUnqualify(this, tenv, cst, ds));
217
0
}
218
219
0
std::string ProcManager::name() const { return "process manager"; }
220
221
106k
const TLong* pidTy(const MonoTypePtr& mty) {
222
106k
  if (const TApp* aty = is<TApp>(mty)) {
223
0
    if (const Prim* tf = is<Prim>(aty->fn())) {
224
0
      if (tf->name() == "process") {
225
0
        if (aty->args().size() == 1) {
226
0
          return is<TLong>(aty->args()[0]);
227
0
        }
228
0
      }
229
0
    }
230
0
  }
231
106k
  return nullptr;
232
106k
}
233
234
0
MonoTypePtr mkPidTy(long pid) {
235
0
  return tapp(primty("process"), list(tlong(pid)));
236
0
}
237
238
}
239